diff --git a/CHANGELOG.md b/CHANGELOG.md index e69db25b1051..2cd0de96249c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0.97.0-beta.x + +- **Breaking change** Add the passing on an explicit `Registry` to all calls to `createType` as well as all codec classes. If you are upgrading form an older version, use the [migration guide](UPGRADING.md) to walk through the changes required. +- **Breaking change** The metadata classes have been moved from `@polkadot/types` into `@polkadot/metadata`. If you explicitly create `Metadata` classes, you now would need to do so with the `import Metadata from '@polkadot/metadata';` This is a decorated class, where the previous metadata is now available on `Metadata.metadata`, alongside decorated `consts.*`', `tx.*` and `query.*`. +- `api.derive.*` now has built-in memomization +- Various fixes and cleanups to `api.derive.*` including era length progress and vesting calculations +- Aligned all types with the latest Polkadot & Substrate + # 0.96.1 - Updated types for latest Kusama diff --git a/UPGRADING.md b/UPGRADING.md index 4a6151285a38..85299975b8b9 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -2,7 +2,91 @@ This is an upgrade guide for users of the API. It does not attempt to detail each version (the [CHANGELOG](CHANGELOG.md) has all the changes between versions), but rather tries to explain the rationale behind major breaking changes and how users of the API should handle this. -While we try to keep the user-facing interfaces as stable as possible, sometimes you just need to make additions to move forward and improve things down the road, as painful as they may be. Like you, we are also users of the API, and eat our own dogfood - and as such, feel any pains introduced first. +While we try to keep the user-facing interfaces as stable as possible, sometimes you just need to make additions to move forward and improve things down the road, as painful as they may be. Like you, we are also users of the API, and eat our own dog food - and as such, feel any pains introduced first. + + +## 0.97.1 (and newer), from 0.90.1 (and older) + +The 0.97 series lays the groundwork to allow type registration to be ties to a specific chain and a specific Api instance. In the past, 2 Api instances in the same process would share types, which mean that you could not connect to 2 independent chains with different types. This is very problematic for Polkadot chains, where the idea is to connect to multiple chains. + +When using the Api, a new `Registry` will be created on using `new Api(...)` or `Api.create(...)` and this will be transparently passed when creating types. In the cases where you create type instances explicitly or create type classes for injection, you would need to make adjustments. + +### Type classes + +In a number of instances, developers are creating classes and making these available for interacting with their chains. For instance, an example of a custom type could be - + +```js +import { Struct, Text, u32 } from '@polkadot/types'; + +export class Preferences extends Struct { + constructor (value?: ahy) { + super({ + name: Text, + id: u32 + }, value); + } + + ... +} +``` + +In the current iteration, the underlying `@polkadot/types` bases structures now require a `Registry` to be passed as the first parameter. This means that the above signature would be adjusted to - + +```js +// the next import is only required for TypeScript +import { Registry } from '@polkadot/types/types'; +import { Struct, Text, u32 } from '@polkadot/types'; + +export class Preferences extends Struct { + constructor (registry: Registry, value?: ahy) { + super(registry, { + name: Text, + id: u32 + }, value); + } + + ... +} +``` + +Where the type is used or returned from the API, the `Registry` will be automatically passed to class creation. + +### createType + +Previously, when creating a type instance such as `BlockNumber`, you would do `api.createType('BlockNumber', )`, this is unchanged. In the cases where you directly import from `@polkadot/types`, the following pattern is required - + +```js +import { createType } from '@polkadot/types'; + +... +const blockNumber = createType(api.registry, 'BlockNumber', 12345); +``` + +In some cases, you would want to explicitly pass a `Registry` interface to the API, instead of relying on it explicitly. This is generally applicable in the cases where you want to use the `createType` independently from the API - + +```js +import { ApiPromise } from '@polkadot/api'; +import { TypeRegistry, createType } from '@polkadot/types'; + +... +const registry = new TypeRegistry(); +const blockNumber = createType(registry, 'BlockNumber', 12345); +const api = await ApiPromise.create({ registry }); +``` + +### Extrinsic metadata + +In some applications, the undocumented `findFunction` has been used to determine the Api has the metadata for a specific extrinsic. The has been exposed on top of `GenericCall`, and it typically used in applications such as signers. Along with the compulsory registry, the above functions have been moved to the `Registry` itself, so if you previously had - + +```js +const { meta, method, section } = GenericCall.findFunction(extrinsic.callIndex); +``` + +You need to change it to - + +```js +const { meta, method, section } = registry.findMetaCall(extrinsic.callIndex); +``` ## 0.90.1 (and newer), from 0.81.1 (and older) @@ -29,10 +113,10 @@ To better align with the actual types from the metadata, and avoid (too much) co The [@polkadot/api](packages/api) has always handled the conversion of types for parameters when making calls or queries. For example, when making a transfer to `BOB` (address), any of the following is valid - -- `api.tx.balances.transfer(BOB, 12345)` - value specied as a number -- `api.tx.balances.transfer(BOB, '12345')` - value specied as a string -- `api.tx.balances.transfer(BOB, '0x3039')` - value specied as a hex -- `api.tx.balances.transfer(BOB, new BN(12345))` - value specied as a [BN](https://github.com/indutny/bn.js/) +- `api.tx.balances.transfer(BOB, 12345)` - value specified as a number +- `api.tx.balances.transfer(BOB, '12345')` - value specified as a string +- `api.tx.balances.transfer(BOB, '0x3039')` - value specified as a hex +- `api.tx.balances.transfer(BOB, new BN(12345))` - value specified as a [BN](https://github.com/indutny/bn.js/) Internally the API will take the input and convert the value into a `Balance`, serialize it using the SCALE codec and transfer it to the node. In some cases users would construct the `Balance` type manually, by importing the class and calling `new` on it. This last approach has now been removed, and where classes are still available (limited reach), discouraged. @@ -108,7 +192,7 @@ export class MyStruct extends Struct { } ``` -Internally the [@polkadot/types](packages/types) package now only defines classes where there are specific encoding logic applied. For all other types, the definitions are done via a JSON-like format and then the TypeScript definitions are generated from these. (In a world where nodes inject types and the type definitions are not needed, this functionality will be useful to allow TS devs to auto-generate type definitions based on what the node defines.) +Internally the [@polkadot/types](packages/types) package now only defines classes where there are specific encoding logic applied. For all other types, the definitions are done via a JSON-like format and then the TypeScript definitions are generated from these. (In a world where nodes inject types and the type definitions are not needed, this functionality will be useful to allow TS developers to auto-generate type definitions based on what the node defines.) ### Signing transactions (Signer interface) diff --git a/packages/api-contract/src/Abi.spec.ts b/packages/api-contract/src/Abi.spec.ts index 6a63a2baabe3..9f8528fe1ccb 100644 --- a/packages/api-contract/src/Abi.spec.ts +++ b/packages/api-contract/src/Abi.spec.ts @@ -2,16 +2,19 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import incrementerAbi from '../test/contracts/incrementer.json'; +import { TypeRegistry } from '@polkadot/types'; +import incrementerAbi from '../test/contracts/incrementer.json'; import { Abi } from '.'; describe('Abi', (): void => { + const registry = new TypeRegistry(); + describe('incrementer', (): void => { let abi: Abi; beforeEach((): void => { - abi = new Abi(incrementerAbi); + abi = new Abi(registry, incrementerAbi); }); it('has the attached methods', (): void => { diff --git a/packages/api-contract/src/Abi.ts b/packages/api-contract/src/Abi.ts index 5c674d9f4d76..d997f03e3ba0 100644 --- a/packages/api-contract/src/Abi.ts +++ b/packages/api-contract/src/Abi.ts @@ -2,7 +2,9 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; import { AbiConstructors, AbiMessages, ContractABI, ContractABIPre, ContractABIFn, InterfaceAbi } from './types'; + import { stringCamelCase } from '@polkadot/util'; import ContractRegistry from './ContractRegistry'; @@ -14,8 +16,8 @@ export default class ContractAbi extends ContractRegistry implements InterfaceAb public readonly messages: AbiMessages; - constructor (abi: ContractABIPre) { - super(abi); + constructor (registry: Registry, abi: ContractABIPre) { + super(registry, abi); [this.abi, this.constructors, this.messages] = this.decodeAbi(abi); } diff --git a/packages/api-contract/src/ContractRegistry.ts b/packages/api-contract/src/ContractRegistry.ts index 6a5fd2e4d710..27dc5685a116 100644 --- a/packages/api-contract/src/ContractRegistry.ts +++ b/packages/api-contract/src/ContractRegistry.ts @@ -2,28 +2,28 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { CodecArg, Constructor, TypeDef } from '@polkadot/types/types'; +import { CodecArg, Constructor, Registry, TypeDef } from '@polkadot/types/types'; import { ContractABIArgBasePre, ContractABIContract, ContractABIContractPre, ContractABIEvent, ContractABIEventPre, ContractABIFn, ContractABIFnArg, ContractABIMessage, ContractABIMessageBase, ContractABIMessagePre, ContractABI, ContractABIMessageCommon, ContractABIPre, ContractABIRange, ContractABIRangePre, ContractABIStorage, ContractABIStorageLayout, ContractABIStorageLayoutPre, ContractABIStoragePre, ContractABIStorageStruct, ContractABIStorageStructPre, ContractABITypePre } from './types'; -import { Compact, u32, createClass, createType, encodeType } from '@polkadot/types'; +import { Compact, u32, createClass, createType, encodeType } from '@polkadot/types'; import { assert, hexToU8a, isNumber, isString, isNull, isObject, isUndefined, stringCamelCase, isHex, hexToNumber } from '@polkadot/util'; import MetaRegistry from './MetaRegistry'; // parse a selector, this can be a number (older) or of [, , ...]. However, // just catch everything (since this is now non-standard for u32 anyway) -function parseSelector (fnname: string, input: ContractABIMessageCommon['selector']): u32 { +function parseSelector (registry: Registry, fnname: string, input: ContractABIMessageCommon['selector']): u32 { if (isNumber(input)) { - return createType('u32', input); + return createType(registry, 'u32', input); } else if (isHex(input)) { - return createType('u32', hexToU8a(input)); + return createType(registry, 'u32', hexToU8a(input)); } else if (typeof input === 'string') { try { const array = JSON.parse(input); assert(array.length === 4, `${fnname}: Invalid selector length`); - return createType('u32', Uint8Array.from( + return createType(registry, 'u32', Uint8Array.from( // the as number[] is here to pacify TS, it doesn't quite know how to handle the cb (array as number[]).map((value: string | number): number => isHex(value) @@ -39,8 +39,9 @@ function parseSelector (fnname: string, input: ContractABIMessageCommon['selecto throw new Error(`${fnname}: Unable to parse selector`); } -function createArgClass (args: ContractABIFnArg[], baseDef: Record): Constructor { +function createArgClass (registry: Registry, args: ContractABIFnArg[], baseDef: Record): Constructor { return createClass( + registry, JSON.stringify( args.reduce((base: Record, { name, type }): Record => { base[name] = type.displayName || encodeType(type); @@ -87,7 +88,7 @@ export default class ContractRegistry extends MetaRegistry { assert(isNumber(name) && isString(this.stringAt(name)), `Expected name for ${fnname}`); assert(isNull(returnType) || (isNumber(returnType.ty) && isObject(this.typeDefAt(returnType.ty))), `Expected return_type for ${fnname}`); - parseSelector(fnname, selector); + parseSelector(this.registry, fnname, selector); this.validateArgs(fnname, message.args); }); } @@ -113,12 +114,13 @@ export default class ContractRegistry extends MetaRegistry { type }; }); - const Clazz = createArgClass(args, isUndefined(message.selector) ? {} : { __selector: 'u32' }); - const baseStruct: { [index: string]: any } = { __selector: isUndefined(message.selector) ? undefined : parseSelector(name, message.selector) }; + const Clazz = createArgClass(this.registry, args, isUndefined(message.selector) ? {} : { __selector: 'u32' }); + const baseStruct: { [index: string]: any } = { __selector: isUndefined(message.selector) ? undefined : parseSelector(this.registry, name, message.selector) }; const encoder = (...params: CodecArg[]): Uint8Array => { assert(params.length === args.length, `Expected ${args.length} arguments to contract ${name}, found ${params.length}`); const u8a = new Clazz( + this.registry, args.reduce((mapped, { name }, index): Record => { mapped[name] = params[index]; diff --git a/packages/api-contract/src/MetaRegistry.spec.ts b/packages/api-contract/src/MetaRegistry.spec.ts index a41a0190e5ed..4c6da68724e5 100644 --- a/packages/api-contract/src/MetaRegistry.spec.ts +++ b/packages/api-contract/src/MetaRegistry.spec.ts @@ -4,6 +4,7 @@ import fs from 'fs'; import path from 'path'; +import { TypeRegistry } from '@polkadot/types'; import MetaRegistry from './MetaRegistry'; @@ -11,9 +12,11 @@ import erc20Abi from '../test/contracts/Erc20.json'; import sharedVecAbi from '../test/contracts/SharedVecV2.json'; describe('MetaRegistry', (): void => { + const registry = new TypeRegistry(); + describe('construction', (): void => { it('initializes from a contract ABI (ERC20)', (): void => { - const metaRegistry = new MetaRegistry(erc20Abi); + const metaRegistry = new MetaRegistry(registry, erc20Abi); fs.writeFile(path.join(__dirname, '../test/compare/erc20.test.json'), JSON.stringify(metaRegistry.typeDefs, null, 2), function (err): void { if (err) throw err; }); @@ -22,7 +25,7 @@ describe('MetaRegistry', (): void => { }); it('initializes from a contract ABI (SharedVec)', (): void => { - const metaRegistry = new MetaRegistry(sharedVecAbi); + const metaRegistry = new MetaRegistry(registry, sharedVecAbi); fs.writeFile(path.join(__dirname, '../test/compare/shared-vec.test.json'), JSON.stringify(metaRegistry.typeDefs, null, 2), function (err): void { if (err) throw err; }); diff --git a/packages/api-contract/src/MetaRegistry.ts b/packages/api-contract/src/MetaRegistry.ts index acb2ab082ea6..75805d062d54 100644 --- a/packages/api-contract/src/MetaRegistry.ts +++ b/packages/api-contract/src/MetaRegistry.ts @@ -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 { MetaRegistryItem, MetaRegistryJson, MetaTypeDefClikeEnum, MetaType, MetaTypeDefEnum, MetaTypeDefEnumVariant, MetaTypeDefEnumVariantStruct, MetaTypeDefEnumVariantTupleStruct, MetaTypeDefEnumVariantUnit, MetaTypeDefStruct, MetaTypeDefStructField, MetaTypeDefTupleStruct, MetaTypeDefUnion, MetaTypeIdCustom, MetaTypeIdVec, MetaTypeIdVecFixed, MetaTypeInfo, StringIndex, TypeDef, TypeDefInfo, TypeIndex } from '@polkadot/types/types'; +import { MetaRegistryItem, MetaRegistryJson, MetaTypeDefClikeEnum, MetaType, MetaTypeDefEnum, MetaTypeDefEnumVariant, MetaTypeDefEnumVariantStruct, MetaTypeDefEnumVariantTupleStruct, MetaTypeDefEnumVariantUnit, MetaTypeDefStruct, MetaTypeDefStructField, MetaTypeDefTupleStruct, MetaTypeDefUnion, MetaTypeIdCustom, MetaTypeIdVec, MetaTypeIdVecFixed, MetaTypeInfo, Registry, StringIndex, TypeDef, TypeDefInfo, TypeIndex } from '@polkadot/types/types'; import { assert } from '@polkadot/util'; import { displayType, withTypeString } from '@polkadot/types'; @@ -34,13 +34,16 @@ function detectedType ({ def, id }: MetaType): MetaTypeInfo { } class MetadataRegistryLookup { + public readonly registry: Registry; + protected _strings: string[] = []; protected _types: MetaType[] = []; public typeDefs: TypeDef[] = []; - constructor ({ registry: { strings, types } }: MetaRegistryJson) { + constructor (registry: Registry, { registry: { strings, types } }: MetaRegistryJson) { + this.registry = registry; this._strings = strings; this._types = types; } @@ -102,8 +105,8 @@ class MetadataRegistryLookup { } export default class MetaRegistry extends MetadataRegistryLookup { - constructor (json: MetaRegistryJson) { - super(json); + constructor (registry: Registry, json: MetaRegistryJson) { + super(registry, json); // Generate TypeDefs for each provided registry type this._types.forEach((_, index: number): void => this.setTypeDefAtIndex(index + 1)); diff --git a/packages/api-contract/src/base/Blueprint.ts b/packages/api-contract/src/base/Blueprint.ts index 96785f703040..514b44ec5a20 100644 --- a/packages/api-contract/src/base/Blueprint.ts +++ b/packages/api-contract/src/base/Blueprint.ts @@ -41,7 +41,7 @@ export default class Blueprint extends BaseWithTx, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod, codeHash: string | Hash) { super(api, abi, decorateMethod); - this.codeHash = createType('Hash', codeHash); + this.codeHash = createType(this.registry, 'Hash', codeHash); } public deployContract (constructorIndex = 0, endowment: number | BN, maxGas: number | BN, ...params: any[]): BlueprintCreate { diff --git a/packages/api-contract/src/base/Contract.ts b/packages/api-contract/src/base/Contract.ts index 699908cbd175..4087117abee6 100644 --- a/packages/api-contract/src/base/Contract.ts +++ b/packages/api-contract/src/base/Contract.ts @@ -44,7 +44,7 @@ export default class Contract extends BaseWithTxAndRpc as === 'rpc' ? (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'rpc'> => { return this.rpcContractsCall( - createType('ContractCallRequest', { + createType(this.registry, 'ContractCallRequest', { origin: account, dest: this.address.toString(), value, @@ -54,7 +54,7 @@ export default class Contract extends BaseWithTxAndRpc ) .pipe( map((result: ContractExecResult): ContractCallOutcome => - this.createOutcome(result, createType('AccountId', account), def, params) + this.createOutcome(result, createType(this.registry, 'AccountId', account), def, params) ) ); } @@ -74,8 +74,8 @@ export default class Contract extends BaseWithTxAndRpc const { data } = result.asSuccess; output = message.returnType - ? formatData(data, message.returnType) - : createType('Data', data); + ? formatData(this.registry, data, message.returnType) + : createType(this.registry, 'Data', data); } return { @@ -92,6 +92,6 @@ export default class Contract extends BaseWithTxAndRpc constructor (api: ApiObject, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod, address: string | AccountId | Address) { super(api, abi, decorateMethod); - this.address = createType('Address', address); + this.address = createType(this.registry, 'Address', address); } } diff --git a/packages/api-contract/src/base/util.ts b/packages/api-contract/src/base/util.ts index 92ed76549358..e3866a75416d 100644 --- a/packages/api-contract/src/base/util.ts +++ b/packages/api-contract/src/base/util.ts @@ -5,6 +5,7 @@ import { ApiTypes, DecorateMethod, DecoratedRpc, SubmittableModuleExtrinsics } from '@polkadot/api/types'; import { ApiObject, ContractABIMessage, ContractABIPre, ContractBase, ContractMessage } from '../types'; import { RpcInterface } from '@polkadot/rpc-core/jsonrpc.types'; +import { Registry } from '@polkadot/types/types'; import { assert, stringCamelCase } from '@polkadot/util'; import Abi from '../Abi'; @@ -16,10 +17,13 @@ export abstract class Base implements ContractBase; + public readonly registry: Registry; + constructor (api: ApiObject, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod) { this.abi = abi instanceof Abi ? abi - : new Abi(abi); + : new Abi(api.registry, abi); + this.registry = api.registry; this.api = api; this.decorateMethod = decorateMethod; } diff --git a/packages/api-contract/src/util.ts b/packages/api-contract/src/util.ts index 5fe9529845b9..6969ed2c2979 100644 --- a/packages/api-contract/src/util.ts +++ b/packages/api-contract/src/util.ts @@ -2,20 +2,21 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Codec, TypeDefInfo, TypeDef } from '@polkadot/types/types'; +import { Codec, Registry, TypeDefInfo, TypeDef } from '@polkadot/types/types'; import { Data, Option, createClass } from '@polkadot/types'; import { createTypeUnsafe } from '@polkadot/types/codec'; -export function formatData (data: Data, { info, type }: TypeDef): Codec { +export function formatData (registry: Registry, data: Data, { info, type }: TypeDef): Codec { const u8a = data.toU8a(); if (info === TypeDefInfo.Option) { return new Option( - createClass(type), - createTypeUnsafe(type, [u8a], true) + registry, + createClass(registry, type), + createTypeUnsafe(registry, type, [u8a], true) ); } - return createTypeUnsafe(type, [u8a], true); + return createTypeUnsafe(registry, type, [u8a], true); } diff --git a/packages/api-derive/src/accounts/idAndIndex.ts b/packages/api-derive/src/accounts/idAndIndex.ts index ba17931070b4..7f1bbb5cc1fa 100644 --- a/packages/api-derive/src/accounts/idAndIndex.ts +++ b/packages/api-derive/src/accounts/idAndIndex.ts @@ -7,9 +7,9 @@ import { AccountId, AccountIndex, Address } from '@polkadot/types/interfaces'; import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; +import { createType } from '@polkadot/types'; import { isU8a } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; -import { createType } from '@polkadot/types'; import { memo } from '../util'; @@ -23,14 +23,14 @@ function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIn : decodeAddress((address || '').toString()); if (decoded.length === 32) { - const accountId = createType('AccountId', decoded); + const accountId = createType(api.registry, 'AccountId', decoded); return api.derive.accounts.idToIndex(accountId).pipe( map((accountIndex): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex) ); } - const accountIndex = createType('AccountIndex', decoded); + const accountIndex = createType(api.registry, 'AccountIndex', decoded); return api.derive.accounts.indexToId(accountIndex).pipe( map((accountId): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex) diff --git a/packages/api-derive/src/accounts/indexToId.ts b/packages/api-derive/src/accounts/indexToId.ts index b7107cab85cf..4bcb2bce2585 100644 --- a/packages/api-derive/src/accounts/indexToId.ts +++ b/packages/api-derive/src/accounts/indexToId.ts @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { ENUMSET_SIZE } from '@polkadot/types/primitive/Generic/AccountIndex'; -import { createType, ClassOf, Vec } from '@polkadot/types'; +import { ClassOf, Vec, createType } from '@polkadot/types'; import { memo } from '../util'; @@ -29,9 +29,9 @@ export function indexToId (api: ApiInterfaceRx): (accountIndex: AccountIndex | s const querySection = api.query.indices || api.query.balances; return memo((_accountIndex: AccountIndex | string): Observable => { - const accountIndex = _accountIndex instanceof ClassOf('AccountIndex') + const accountIndex = _accountIndex instanceof ClassOf(api.registry, 'AccountIndex') ? _accountIndex - : createType('AccountIndex', _accountIndex); + : createType(api.registry, 'AccountIndex', _accountIndex); return querySection.enumSet>(accountIndex.div(ENUMSET_SIZE)).pipe( startWith([]), diff --git a/packages/api-derive/src/accounts/indexes.ts b/packages/api-derive/src/accounts/indexes.ts index 850cf042c424..140457473b06 100644 --- a/packages/api-derive/src/accounts/indexes.ts +++ b/packages/api-derive/src/accounts/indexes.ts @@ -9,7 +9,7 @@ import { AccountIndexes } from '../types'; import { Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { ENUMSET_SIZE } from '@polkadot/types/primitive/Generic/AccountIndex'; -import { createType, Vec } from '@polkadot/types'; +import { Vec, createType } from '@polkadot/types'; import { memo } from '../util'; @@ -47,7 +47,7 @@ export function indexes (api: ApiInterfaceRx): () => Observable // 64 (0..63 in first) is [1][0] (the first index value in set 2) const index = (outerIndex * enumsetSize) + innerIndex; - result[accountId.toString()] = createType('AccountIndex', index); + result[accountId.toString()] = createType(api.registry, 'AccountIndex', index); }); return result; diff --git a/packages/api-derive/src/balances/all.ts b/packages/api-derive/src/balances/all.ts index fcc67187ec5d..1160d8e695d4 100644 --- a/packages/api-derive/src/balances/all.ts +++ b/packages/api-derive/src/balances/all.ts @@ -17,8 +17,8 @@ import { memo } from '../util'; type ResultBalance = [Balance, Balance, BalanceLock[], Option]; type Result = [AccountId, BlockNumber, ResultBalance, Index]; -function calcBalances ([accountId, bestNumber, [freeBalance, reservedBalance, locks, vesting], accountNonce]: Result): DerivedBalances { - let lockedBalance = createType('Balance'); +function calcBalances (api: ApiInterfaceRx, [accountId, bestNumber, [freeBalance, reservedBalance, locks, vesting], accountNonce]: Result): DerivedBalances { + let lockedBalance = createType(api.registry, 'Balance'); let lockedBreakdown: BalanceLock[] = []; if (Array.isArray(locks)) { @@ -27,15 +27,15 @@ function calcBalances ([accountId, bestNumber, [freeBalance, reservedBalance, lo // get the maximum of the locks according to https://github.com/paritytech/substrate/blob/master/srml/balances/src/lib.rs#L699 if (lockedBreakdown.length) { - lockedBalance = createType('Balance', bnMax(...lockedBreakdown.map(({ amount }): Balance => amount))); + lockedBalance = createType(api.registry, 'Balance', bnMax(...lockedBreakdown.map(({ amount }): Balance => amount))); } } // Calculate the vesting balances, // - offset = balance locked at genesis, // - perBlock is the unlock amount - const { offset: vestingTotal, perBlock } = vesting.unwrapOr(createType('VestingSchedule')); - const vestedBalance = createType('Balance', perBlock.mul(bestNumber)); + const { offset: vestingTotal, perBlock } = vesting.unwrapOr(createType(api.registry, 'VestingSchedule')); + const vestedBalance = createType(api.registry, 'Balance', perBlock.mul(bestNumber)); const isVesting = vestedBalance.lt(vestingTotal); // The available balance & vested has an interplay here @@ -46,7 +46,7 @@ function calcBalances ([accountId, bestNumber, [freeBalance, reservedBalance, lo // i.e. (balance >= 200 && balance >= 300) == (balance >= 300) // "" const floating = freeBalance.sub(lockedBalance); - const availableBalance = createType('Balance', bnMax(new BN(0), isVesting && floating.gt(vestedBalance) ? vestedBalance : floating)); + const availableBalance = createType(api.registry, 'Balance', bnMax(new BN(0), isVesting && floating.gt(vestedBalance) ? vestedBalance : floating)); return { accountId, @@ -59,7 +59,7 @@ function calcBalances ([accountId, bestNumber, [freeBalance, reservedBalance, lo reservedBalance, vestedBalance, vestingTotal, - votingBalance: createType('Balance', freeBalance.add(reservedBalance)) + votingBalance: createType(api.registry, 'Balance', freeBalance.add(reservedBalance)) }; } @@ -103,9 +103,9 @@ export function all (api: ApiInterfaceRx): (address: AccountIndex | AccountId | // : api.query.system.accountNonce(accountId) api.query.system.accountNonce(accountId) ]) - : of([createType('AccountId'), createType('BlockNumber'), [createType('Balance'), createType('Balance'), createType('Vec'), createType('Option', null)], createType('Index')]) + : of([createType(api.registry, 'AccountId'), createType(api.registry, 'BlockNumber'), [createType(api.registry, 'Balance'), createType(api.registry, 'Balance'), createType(api.registry, 'Vec'), createType(api.registry, 'Option', null)], createType(api.registry, 'Index')]) ) ), - map(calcBalances) + map((result): DerivedBalances => calcBalances(api, result)) )); } diff --git a/packages/api-derive/src/chain/bestNumberLag.ts b/packages/api-derive/src/chain/bestNumberLag.ts index 2f5e10827eac..7d205bba8c7b 100644 --- a/packages/api-derive/src/chain/bestNumberLag.ts +++ b/packages/api-derive/src/chain/bestNumberLag.ts @@ -31,7 +31,7 @@ export function bestNumberLag (api: ApiInterfaceRx): () => Observable - createType('BlockNumber', bestNumber.sub(bestNumberFinalized)) + createType(api.registry, 'BlockNumber', bestNumber.sub(bestNumberFinalized)) ) )); } diff --git a/packages/api-derive/src/chain/getHeader.ts b/packages/api-derive/src/chain/getHeader.ts index af467e028c2f..d9fce599368a 100644 --- a/packages/api-derive/src/chain/getHeader.ts +++ b/packages/api-derive/src/chain/getHeader.ts @@ -36,7 +36,7 @@ export function getHeader (api: ApiInterfaceRx): (hash: Uint8Array | string) => : of([]) ]).pipe( map(([header, validators]): HeaderExtended => - new HeaderExtended(header, validators) + new HeaderExtended(api.registry, header, validators) ), catchError((): Observable => // where rpc.chain.getHeader throws, we will land here - it can happen that diff --git a/packages/api-derive/src/chain/subscribeNewHeads.ts b/packages/api-derive/src/chain/subscribeNewHeads.ts index 67b9bb8a7aa7..0dcf4b3d20d8 100644 --- a/packages/api-derive/src/chain/subscribeNewHeads.ts +++ b/packages/api-derive/src/chain/subscribeNewHeads.ts @@ -30,7 +30,7 @@ export function subscribeNewHeads (api: ApiInterfaceRx): () => Observable
- new HeaderExtended(header, validators) + new HeaderExtended(api.registry, header, validators) ) )); } diff --git a/packages/api-derive/src/democracy/referendumInfo.ts b/packages/api-derive/src/democracy/referendumInfo.ts index 925e29c9cbd3..8d50c47ab7df 100644 --- a/packages/api-derive/src/democracy/referendumInfo.ts +++ b/packages/api-derive/src/democracy/referendumInfo.ts @@ -14,16 +14,17 @@ import { isNull } from '@polkadot/util'; import { ReferendumInfoExtended } from '../type'; import { memo } from '../util'; -export function constructInfo (index: BN | number, optionInfo?: Option): Option { +export function constructInfo (api: ApiInterfaceRx, index: BN | number, optionInfo?: Option): Option { const info = optionInfo ? optionInfo.unwrapOr(null) : null; return new Option( + api.registry, ReferendumInfoExtended, isNull(info) ? null - : new ReferendumInfoExtended(info, index) + : new ReferendumInfoExtended(api.registry, info, index) ); } @@ -31,7 +32,7 @@ export function referendumInfo (api: ApiInterfaceRx): (index: BN | number) => Ob return memo((index: BN | number): Observable> => api.query.democracy.referendumInfoOf>(index).pipe( map((optionInfo): Option => - constructInfo(index, optionInfo) + constructInfo(api, index, optionInfo) ) )); } diff --git a/packages/api-derive/src/democracy/referendumInfos.ts b/packages/api-derive/src/democracy/referendumInfos.ts index 080771e2b140..e82c30b675b1 100644 --- a/packages/api-derive/src/democracy/referendumInfos.ts +++ b/packages/api-derive/src/democracy/referendumInfos.ts @@ -23,7 +23,7 @@ export function referendumInfos (api: ApiInterfaceRx): (ids?: (BN | number)[]) = ).pipe( map((infos): Option[] => ids.map((id, index): Option => - constructInfo(id, infos[index]) + constructInfo(api, id, infos[index]) ) ) )); diff --git a/packages/api-derive/src/democracy/referendumVotesFor.ts b/packages/api-derive/src/democracy/referendumVotesFor.ts index 681ab8e580e8..77ac0e4499f9 100644 --- a/packages/api-derive/src/democracy/referendumVotesFor.ts +++ b/packages/api-derive/src/democracy/referendumVotesFor.ts @@ -26,8 +26,8 @@ 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'), - vote: votes[index] || createType('Vote') + balance: balances[index].votingBalance || createType(api.registry, 'Balance'), + vote: votes[index] || createType(api.registry, 'Vote') } as unknown as DerivedReferendumVote)) ) )); diff --git a/packages/api-derive/src/elections/info.ts b/packages/api-derive/src/elections/info.ts index 1050c438d348..6770e6d5de06 100644 --- a/packages/api-derive/src/elections/info.ts +++ b/packages/api-derive/src/elections/info.ts @@ -16,13 +16,13 @@ import { memo } from '../util'; type ResultElectionsInner = [u32, u32, Vec>, SetIndex, BlockNumber, VoteIndex, SetIndex]; type ResultElections = [Vec, ResultElectionsInner]; -function deriveElections ([candidates, [candidateCount, desiredSeats, members, nextVoterSet, termDuration, voteCount, voterCount]]: ResultElections): DerivedElectionsInfo { +function deriveElections (api: ApiInterfaceRx, [candidates, [candidateCount, desiredSeats, members, nextVoterSet, termDuration, voteCount, voterCount]]: ResultElections): DerivedElectionsInfo { return { candidates, candidateCount, desiredSeats, nextVoterSet, - members: members.map(([accountId]): [AccountId, Balance] => [accountId, createType('Balance')]), + members: members.map(([accountId]): [AccountId, Balance] => [accountId, createType(api.registry, 'Balance')]), runnersUp: [], termDuration, voteCount, @@ -43,13 +43,13 @@ function queryElections (api: ApiInterfaceRx): Observable api.query.elections.voteCount, api.query.elections.voterCount ]) - ]).pipe(map(deriveElections)); + ]).pipe(map((result): DerivedElectionsInfo => deriveElections(api, result))); } -function derivePhragmen (candidates: AccountId[], members: [AccountId, Balance][], runnersUp: [AccountId, Balance][], candidacyBond: Balance, desiredSeats: u32, termDuration: BlockNumber, votingBond: Balance): DerivedElectionsInfo { +function derivePhragmen (api: ApiInterfaceRx, candidates: AccountId[], members: [AccountId, Balance][], runnersUp: [AccountId, Balance][], candidacyBond: Balance, desiredSeats: u32, termDuration: BlockNumber, votingBond: Balance): DerivedElectionsInfo { return { candidates, - candidateCount: createType('u32', candidates.length), + candidateCount: createType(api.registry, 'u32', candidates.length), candidacyBond, desiredSeats, members, @@ -69,6 +69,7 @@ function queryPhragmen (api: ApiInterfaceRx): Observable { api.query.electionsPhragmen.runnersUp>>() ]).pipe( map(([candidates, members, runnersUp]): DerivedElectionsInfo => derivePhragmen( + api, candidates, members, runnersUp, diff --git a/packages/api-derive/src/elections/voterPositions.ts b/packages/api-derive/src/elections/voterPositions.ts index 2d609a8e6f95..5381cc17d8f9 100644 --- a/packages/api-derive/src/elections/voterPositions.ts +++ b/packages/api-derive/src/elections/voterPositions.ts @@ -29,7 +29,7 @@ function queryElections (api: ApiInterfaceRx): Observable result[accountId.toString()] = { globalIndex: (api.consts.elections.voterSetSize as u32).muln(setIndex).addn(index), index: new BN(index), - setIndex: createType('SetIndex', setIndex) + setIndex: createType(api.registry, 'SetIndex', setIndex) }; } }); diff --git a/packages/api-derive/src/elections/voters.ts b/packages/api-derive/src/elections/voters.ts index 40f19901441d..ffb0be322fc0 100644 --- a/packages/api-derive/src/elections/voters.ts +++ b/packages/api-derive/src/elections/voters.ts @@ -16,10 +16,11 @@ function queryElections (api: ApiInterfaceRx): Observable { return api.derive.elections.voterPositions().pipe( map((voterPositions: DerivedVoterPositions): Vec => createType( + api.registry, 'Vec', Object.entries(voterPositions) .sort((a, b): number => a[1].globalIndex.cmp(b[1].globalIndex)) - .map(([accountId]): AccountId => createType('AccountId', accountId)) + .map(([accountId]): AccountId => createType(api.registry, 'AccountId', accountId)) ) ) ); diff --git a/packages/api-derive/src/index.spec.ts b/packages/api-derive/src/index.spec.ts index 7b6176ba526c..f92f9af2addb 100644 --- a/packages/api-derive/src/index.spec.ts +++ b/packages/api-derive/src/index.spec.ts @@ -5,6 +5,7 @@ import { Observable, from } from 'rxjs'; import ApiRx from '@polkadot/api/rx/Api'; import MockProvider from '@polkadot/rpc-provider/mock'; +import { TypeRegistry } from '@polkadot/types'; import { ExactDerive } from '.'; @@ -26,8 +27,10 @@ const testFunction = (api: ApiRx): any => { }; describe('derive', (): void => { + const registry = new TypeRegistry(); + describe('builtin', (): void => { - const api = new ApiRx({ provider: new MockProvider() }); + const api = new ApiRx({ provider: new MockProvider(registry), registry }); beforeAll((done): void => { api.isReady.subscribe((): void => done()); @@ -77,7 +80,8 @@ describe('derive', (): void => { test: (): any => (): Observable => from([1, 2, 3]) } }, - provider: new MockProvider() + provider: new MockProvider(registry), + registry }); beforeAll((done): void => { diff --git a/packages/api-derive/src/session/indexes.ts b/packages/api-derive/src/session/indexes.ts index d710a2a77aea..59c1b15b23fe 100644 --- a/packages/api-derive/src/session/indexes.ts +++ b/packages/api-derive/src/session/indexes.ts @@ -22,7 +22,7 @@ export function indexes (api: ApiInterfaceRx): () => Observable ({ currentIndex, currentEra, validatorCount diff --git a/packages/api-derive/src/session/info.ts b/packages/api-derive/src/session/info.ts index 0358a6a08b35..5a5d419edab0 100644 --- a/packages/api-derive/src/session/info.ts +++ b/packages/api-derive/src/session/info.ts @@ -20,8 +20,8 @@ type ResultType = [boolean, u64, SessionIndex]; type Result = [ResultType, DeriveSessionIndexes, ResultSlots]; // internal helper to just split the logic - take all inputs, do the calculations and combine -function createDerivedV1 ([bestNumber, { currentIndex, validatorCount }, [_lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: ResultV1): DerivedSessionInfo { - const lastLengthChange = _lastLengthChange?.unwrapOr(null) || createType('BlockNumber'); +function createDerivedV1 (api: ApiInterfaceRx, [bestNumber, { currentIndex, validatorCount }, [_lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: ResultV1): DerivedSessionInfo { + const lastLengthChange = _lastLengthChange?.unwrapOr(null) || createType(api.registry, 'BlockNumber'); const sessionProgress = bestNumber .sub(lastLengthChange) .add(sessionLength) @@ -34,21 +34,21 @@ function createDerivedV1 ([bestNumber, { currentIndex, validatorCount }, [_lastL .add(sessionProgress); return { - currentEra: createType('EraIndex', currentEra), + currentEra: createType(api.registry, 'EraIndex', currentEra), currentIndex, - eraLength: createType('BlockNumber', sessionLength.mul(sessionsPerEra)), - eraProgress: createType('BlockNumber', eraProgress), + eraLength: createType(api.registry, 'BlockNumber', sessionLength.mul(sessionsPerEra)), + eraProgress: createType(api.registry, 'BlockNumber', eraProgress), isEpoch: false, lastEraLengthChange, lastLengthChange, sessionLength, sessionsPerEra, - sessionProgress: createType('BlockNumber', sessionProgress), + sessionProgress: createType(api.registry, 'BlockNumber', sessionProgress), validatorCount }; } -function createDerivedLatest ([[hasBabe, epochDuration, sessionsPerEra], { currentIndex, currentEra, validatorCount }, [currentSlot, epochIndex, epochOrGenesisStartSlot, currentEraStartSessionIndex]]: Result): DerivedSessionInfo { +function createDerivedLatest (api: ApiInterfaceRx, [[hasBabe, epochDuration, sessionsPerEra], { currentIndex, currentEra, validatorCount }, [currentSlot, epochIndex, epochOrGenesisStartSlot, currentEraStartSessionIndex]]: Result): DerivedSessionInfo { const epochStartSlot = epochIndex.mul(epochDuration).add(epochOrGenesisStartSlot); const sessionProgress = currentSlot.sub(epochStartSlot); const eraProgress = currentIndex.sub(currentEraStartSessionIndex).mul(epochDuration).add(sessionProgress); @@ -56,14 +56,14 @@ function createDerivedLatest ([[hasBabe, epochDuration, sessionsPerEra], { curre return { currentEra, currentIndex, - eraLength: createType('BlockNumber', sessionsPerEra.mul(epochDuration)), - eraProgress: createType('BlockNumber', eraProgress), + eraLength: createType(api.registry, 'BlockNumber', sessionsPerEra.mul(epochDuration)), + eraProgress: createType(api.registry, 'BlockNumber', eraProgress), isEpoch: hasBabe, - lastEraLengthChange: createType('BlockNumber'), - lastLengthChange: createType('BlockNumber', epochStartSlot), + lastEraLengthChange: createType(api.registry, 'BlockNumber'), + lastLengthChange: createType(api.registry, 'BlockNumber', epochStartSlot), sessionLength: epochDuration, sessionsPerEra, - sessionProgress: createType('BlockNumber', sessionProgress), + sessionProgress: createType(api.registry, 'BlockNumber', sessionProgress), validatorCount }; } @@ -79,17 +79,17 @@ function infoV1 (api: ApiInterfaceRx): Observable { api.query.staking.sessionsPerEra ]) ]).pipe( - map(createDerivedV1) + map((result): DerivedSessionInfo => createDerivedV1(api, result)) ); } function infoLatestAura (api: ApiInterfaceRx): Observable { return api.derive.session.indexes().pipe( map((indexes): DerivedSessionInfo => - createDerivedLatest([ - [false, createType('u64', 1), api.consts.staking.sessionsPerEra], + createDerivedLatest(api, [ + [false, createType(api.registry, 'u64', 1), api.consts.staking.sessionsPerEra], indexes, - [createType('u64', 1), createType('u64', 1), createType('u64', 1), createType('SessionIndex', 1)] + [createType(api.registry, 'u64', 1), createType(api.registry, 'u64', 1), createType(api.registry, 'u64', 1), createType(api.registry, 'SessionIndex', 1)] ]) ) ); @@ -106,7 +106,7 @@ function infoLatestBabe (api: ApiInterfaceRx): Observable { ]) ]).pipe( map(([indexes, slots]: [DeriveSessionIndexes, ResultSlots]): DerivedSessionInfo => - createDerivedLatest([ + createDerivedLatest(api, [ [true, api.consts.babe.epochDuration, api.consts.staking.sessionsPerEra], indexes, slots diff --git a/packages/api-derive/src/staking/info.ts b/packages/api-derive/src/staking/info.ts index 7486f1ac00e8..e6a6f4d017ca 100644 --- a/packages/api-derive/src/staking/info.ts +++ b/packages/api-derive/src/staking/info.ts @@ -46,11 +46,11 @@ function groupByEra (list: UnlockChunk[]): Record { } // calculate the remaining blocks in a specific unlock era -function remainingBlocks (era: BN, sessionInfo: DerivedSessionInfo): BlockNumber { +function remainingBlocks (api: ApiInterfaceRx, era: BN, sessionInfo: DerivedSessionInfo): BlockNumber { const remaining = era.sub(sessionInfo.currentEra); // on the Rust side the current-era >= era-for-unlock (removal done on >) - return createType('BlockNumber', remaining.gtn(0) + return createType(api.registry, 'BlockNumber', remaining.gtn(0) ? remaining .subn(1) .mul(sessionInfo.eraLength) @@ -59,13 +59,13 @@ function remainingBlocks (era: BN, sessionInfo: DerivedSessionInfo): BlockNumber ); } -function calculateUnlocking (stakingLedger: StakingLedger | undefined, sessionInfo: DerivedSessionInfo): DerivedUnlocking[] | undefined { +function calculateUnlocking (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, sessionInfo: DerivedSessionInfo): DerivedUnlocking[] | undefined { if (isUndefined(stakingLedger)) { return undefined; } const unlockingChunks = stakingLedger.unlocking.filter(({ era }): boolean => - remainingBlocks(era.unwrap(), sessionInfo).gtn(0) + remainingBlocks(api, era.unwrap(), sessionInfo).gtn(0) ); if (!unlockingChunks.length) { @@ -75,20 +75,20 @@ function calculateUnlocking (stakingLedger: StakingLedger | undefined, sessionIn // group the unlock chunks that have the same era and sum their values const groupedResult = groupByEra(unlockingChunks); const results = Object.entries(groupedResult).map(([eraString, value]): DerivedUnlocking => ({ - value: createType('Balance', value), - remainingBlocks: remainingBlocks(new BN(eraString), sessionInfo) + value: createType(api.registry, 'Balance', value), + remainingBlocks: remainingBlocks(api, new BN(eraString), sessionInfo) })); return results.length ? results : undefined; } -function redeemableSum (stakingLedger: StakingLedger | undefined, sessionInfo: DerivedSessionInfo): Balance { +function redeemableSum (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, sessionInfo: DerivedSessionInfo): Balance { if (isUndefined(stakingLedger)) { - return createType('Balance'); + return createType(api.registry, 'Balance'); } - return createType('Balance', stakingLedger.unlocking.reduce((total, { era, value }): BN => { - return remainingBlocks(era.unwrap(), sessionInfo).eqn(0) + return createType(api.registry, 'Balance', stakingLedger.unlocking.reduce((total, { era, value }): BN => { + return remainingBlocks(api, era.unwrap(), sessionInfo).eqn(0) ? total.add(value.unwrap()) : total; }, new BN(0))); @@ -119,22 +119,24 @@ function unwrapSessionIds (stashId: AccountId, queuedKeys: Option | [ }; } -function parseResult ({ accountId, controllerId, stashId, sessionInfo, recentlyOffline = {}, nextKeyFor = createType('Option', null), queuedKeys, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs, nextKeys = createType('Option', null) }: ParseInput): DerivedStaking { +function parseResult (api: ApiInterfaceRx, { accountId, controllerId, stashId, sessionInfo, recentlyOffline = {}, nextKeyFor, queuedKeys, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs, nextKeys }: ParseInput): DerivedStaking { const _stakingLedger = stakingLedger.unwrapOr(undefined); + const _nextKeyFor = nextKeyFor || createType(api.registry, 'Option', null); + const _nextKeys = nextKeys || createType(api.registry, 'Option', null); return { accountId, controllerId, nominators, offline: recentlyOffline[stashId.toString()], - redeemable: redeemableSum(_stakingLedger, sessionInfo), + redeemable: redeemableSum(api, _stakingLedger, sessionInfo), rewardDestination, stakers, stakingLedger: _stakingLedger, stashId, - unlocking: calculateUnlocking(_stakingLedger, sessionInfo), + unlocking: calculateUnlocking(api, _stakingLedger, sessionInfo), validatorPrefs, - ...unwrapSessionIds(stashId, queuedKeys || nextKeyFor, nextKeys) + ...unwrapSessionIds(stashId, queuedKeys || _nextKeyFor, _nextKeys) }; } @@ -153,7 +155,7 @@ function retrieveInfoV1 (api: ApiInterfaceRx, accountId: AccountId, stashId: Acc [api.query.staking.validators, stashId] ]) as Observable ]).pipe(map(([sessionInfo, recentlyOffline, [nextKeyFor, stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs]]]: [DerivedSessionInfo, DerivedRecentlyOffline, MultiResultV1]): DerivedStaking => - parseResult({ + parseResult(api, { accountId, controllerId, stashId, sessionInfo, recentlyOffline, nextKeyFor, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs }) )); @@ -182,7 +184,7 @@ function retrieveInfoV2 (api: ApiInterfaceRx, accountId: AccountId, stashId: Acc : [] : (_nominators as [Vec])[0]; - return parseResult({ + return parseResult(api, { accountId, controllerId, stashId, sessionInfo, queuedKeys, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs, nextKeys }); })); @@ -221,5 +223,5 @@ export function info (api: ApiInterfaceRx): (_accountId: Uint8Array | string) => : retrieveV1; return memo((accountId: Uint8Array | string): Observable => - query(api, createType('AccountId', accountId))); + query(api, createType(api.registry, 'AccountId', accountId))); } diff --git a/packages/api-derive/src/staking/overview.ts b/packages/api-derive/src/staking/overview.ts index 877d67e350b8..61a4b167370b 100644 --- a/packages/api-derive/src/staking/overview.ts +++ b/packages/api-derive/src/staking/overview.ts @@ -27,7 +27,7 @@ export function overview (api: ApiInterfaceRx): () => Observable(currentEra) - : of(createType('EraPoints')) + : of(createType(api.registry, 'EraPoints')) ]) ), map(([{ currentElected, currentEra, currentIndex, validators, validatorCount }, eraPoints]): DerivedStakingOverview => ({ diff --git a/packages/api-derive/src/type/HeaderExtended.ts b/packages/api-derive/src/type/HeaderExtended.ts index 49d8b5f78fb7..4edec0c1671a 100644 --- a/packages/api-derive/src/type/HeaderExtended.ts +++ b/packages/api-derive/src/type/HeaderExtended.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AccountId, Header } from '@polkadot/types/interfaces'; -import { AnyJsonObject, Constructor } from '@polkadot/types/types'; +import { AnyJsonObject, Constructor, Registry } from '@polkadot/types/types'; import runtimeTypes from '@polkadot/types/interfaces/runtime/definitions'; import { Struct } from '@polkadot/types'; @@ -19,8 +19,8 @@ const _Header: Constructor
= Struct.with(runtimeTypes.types.Header as an export default class HeaderExtended extends _Header { private _author?: AccountId; - constructor (header?: Header, sessionValidators?: AccountId[]) { - super(header); + constructor (registry: Registry, header?: Header, sessionValidators?: AccountId[]) { + super(registry, header); this._author = this.extractAuthor(sessionValidators); } diff --git a/packages/api-derive/src/type/ReferendumInfoExtended.ts b/packages/api-derive/src/type/ReferendumInfoExtended.ts index 6d90309aedfd..b759dfde2334 100644 --- a/packages/api-derive/src/type/ReferendumInfoExtended.ts +++ b/packages/api-derive/src/type/ReferendumInfoExtended.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ReferendumIndex, ReferendumInfo } from '@polkadot/types/interfaces/democracy'; -import { AnyJsonObject, Constructor } from '@polkadot/types/types'; +import { AnyJsonObject, Constructor, Registry } from '@polkadot/types/types'; import BN from 'bn.js'; import democracyTypes from '@polkadot/types/interfaces/democracy/definitions'; @@ -20,12 +20,12 @@ const _ReferendumInfo: Constructor = Struct.with(democracyTypes. export default class ReferendumInfoExtended extends _ReferendumInfo { private _index: ReferendumIndex; - constructor (value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) { - super(value); + constructor (registry: Registry, value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) { + super(registry, value); this._index = value instanceof ReferendumInfoExtended ? value.index - : createType('ReferendumIndex', index); + : createType(registry, 'ReferendumIndex', index); } /** diff --git a/packages/api-derive/src/util/approvalFlagToBools.spec.ts b/packages/api-derive/src/util/approvalFlagToBools.spec.ts index 86880f0dab7d..d2a46901c2a3 100644 --- a/packages/api-derive/src/util/approvalFlagToBools.spec.ts +++ b/packages/api-derive/src/util/approvalFlagToBools.spec.ts @@ -4,11 +4,13 @@ import { ApprovalFlag } from '@polkadot/types/interfaces'; -import { createType } from '@polkadot/types'; +import { createType, TypeRegistry } from '@polkadot/types'; import { approvalFlagsToBools } from './approvalFlagsToBools'; describe('approvalFlagsToBools', (): void => { + const registry = new TypeRegistry(); + it('translates and empty array to empty', (): void => { expect( approvalFlagsToBools([] as ApprovalFlag[]) @@ -18,7 +20,7 @@ describe('approvalFlagsToBools', (): void => { it('translates a single input', (): void => { expect( approvalFlagsToBools([ - createType('ApprovalFlag', 0b1010) + createType(registry, 'ApprovalFlag', 0b1010) ]) ).toEqual([false, true, false, true]); }); @@ -26,8 +28,8 @@ describe('approvalFlagsToBools', (): void => { it('translates multiple inputs', (): void => { expect( approvalFlagsToBools([ - createType('ApprovalFlag', 0b0000), - createType('ApprovalFlag', 0b1100) + createType(registry, 'ApprovalFlag', 0b0000), + createType(registry, 'ApprovalFlag', 0b1100) ]) ).toEqual([false, false, false, true, true]); }); diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index 33d5e5a5f86b..59832b37035d 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -5,7 +5,7 @@ import { Constants, Storage } from '@polkadot/metadata/Decorated/types'; import { RpcInterface } from '@polkadot/rpc-core/jsonrpc.types'; import { Call, Hash, RuntimeVersion } from '@polkadot/types/interfaces'; -import { AnyFunction, CallFunction, Codec, CodecArg as Arg, ModulesWithCalls } from '@polkadot/types/types'; +import { AnyFunction, CallFunction, Codec, CodecArg as Arg, ModulesWithCalls, Registry } from '@polkadot/types/types'; import { SubmittableExtrinsic } from '../submittable/types'; import { ApiInterfaceRx, ApiOptions, ApiTypes, @@ -20,7 +20,7 @@ import { map, switchMap } from 'rxjs/operators'; import decorateDerive, { ExactDerive } from '@polkadot/api-derive'; import RpcCore from '@polkadot/rpc-core'; import { WsProvider } from '@polkadot/rpc-provider'; -import { Metadata, Null, u64 } from '@polkadot/types'; +import { Metadata, Null, u64, TypeRegistry } from '@polkadot/types'; import Linkage, { LinkageResult } from '@polkadot/types/codec/Linkage'; import { DEFAULT_VERSION as EXTRINSIC_DEFAULT_VERSION } from '@polkadot/types/primitive/Extrinsic/constants'; import { StorageEntry } from '@polkadot/types/primitive/StorageKey'; @@ -39,6 +39,8 @@ interface MetaDecoration { } export default abstract class Decorate extends Events { + public readonly registry: Registry; + // HACK Use BN import so decorateDerive works... yes, wtf. protected __phantom = new BN(0); @@ -113,6 +115,8 @@ export default abstract class Decorate extends Events constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod) { super(); + this.registry = options.registry || new TypeRegistry(); + const thisProvider = options.source ? options.source._rpcCore.provider.clone() : (options.provider || new WsProvider()); @@ -120,9 +124,10 @@ export default abstract class Decorate extends Events this.decorateMethod = decorateMethod; this._options = options; this._type = type; - this._rpcCore = new RpcCore(thisProvider, this._options.rpc); + this._rpcCore = new RpcCore(this.registry, thisProvider, this._options.rpc); this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected()); this._rx.hasSubscriptions = this._rpcCore.provider.hasSubscriptions; + this._rx.registry = this.registry; } /** @@ -328,8 +333,8 @@ export default abstract class Decorate extends Events } const nextResult = vals.length - ? new LinkageResult([keys[0].constructor as any, keys], [vals[0].constructor as any, vals]) - : new LinkageResult([Null, []], [Null, []]); + ? new LinkageResult(this.registry, [keys[0].constructor as any, keys], [vals[0].constructor as any, vals]) + : new LinkageResult(this.registry, [Null, []], [Null, []]); // we set our result into a subject so we have a single observable for // which the value changes over time. Initially create, follow-up next diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index cc6430fd0ac8..4e935cdb35be 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -7,7 +7,7 @@ import { RegistryTypes } from '@polkadot/types/types'; import { ApiBase, ApiOptions, ApiTypes, DecorateMethod } from '../types'; import DecoratedMeta from '@polkadot/metadata'; -import { GenericCall, GenericEvent, Metadata, u32 as U32 } from '@polkadot/types'; +import { Metadata, u32 as U32 } from '@polkadot/types'; import { LATEST_EXTRINSIC_VERSION } from '@polkadot/types/primitive/Extrinsic/Extrinsic'; import { logger } from '@polkadot/util'; import { cryptoWaitReady, setSS58Format } from '@polkadot/util-crypto'; @@ -16,7 +16,6 @@ import addressDefaults from '@polkadot/util-crypto/address/defaults'; import Decorate from './Decorate'; const KEEPALIVE_INTERVAL = 15000; -const DEFAULT_SS58 = new U32(addressDefaults.prefix); // these are override types for polkadot chains // NOTE The SessionKeys definition for Polkadot and Substrate (OpaqueKeys @@ -141,7 +140,7 @@ export default abstract class Init extends Decorate extends Decorate extends Decorate extends Init (type: K, ...params: any[]): InterfaceRegistry[K] { - return createType(type, ...params); + public createType = (type: K, ...params: any[]): InterfaceRegistry[K] => { + return createType(this.registry, type, ...params); } /** @@ -242,14 +242,14 @@ export default abstract class ApiBase extends Init { function types (): void { // check correct types with `createType` - const balance = createType('Balance', 2); - const gas = createType('Gas', 2); - const compact = createType('Compact', 2); - // const random = createType('RandomType', 2); // This one should deliberately show a TS error + const balance = createType(registry, 'Balance', 2); + const gas = createType(registry, 'Gas', 2); + const compact = createType(registry, 'Compact', 2); + // const random = createType(registry, 'RandomType', 2); // This one should deliberately show a TS error - const gasUnsafe = createTypeUnsafe('Gas', [2]); - const overriddenUnsafe = createTypeUnsafe
('Gas', [2]); + const gasUnsafe = createTypeUnsafe(registry, 'Gas', [2]); + const overriddenUnsafe = createTypeUnsafe
(registry, 'Gas', [2]); console.log(balance, gas, compact, gasUnsafe, overriddenUnsafe); } diff --git a/packages/api/src/promise/Api.spec.ts b/packages/api/src/promise/Api.spec.ts index 5af17b0af081..2e3c64d533fc 100644 --- a/packages/api/src/promise/Api.spec.ts +++ b/packages/api/src/promise/Api.spec.ts @@ -4,30 +4,31 @@ import testingPairs from '@polkadot/keyring/testingPairs'; import Mock from '@polkadot/rpc-provider/mock/index'; -import { createType } from '@polkadot/types'; +import { createType, TypeRegistry } from '@polkadot/types'; import { ApiPromise } from '..'; import { SingleAccountSigner } from '../../test/util'; import { ApiOptions } from '../types'; describe('ApiPromise', (): void => { + const registry = new TypeRegistry(); const keyring = testingPairs({ type: 'ed25519' }); let provider: Mock; beforeEach((): void => { jest.setTimeout(3000000); - provider = new Mock(); + provider = new Mock(registry); }); describe('initialization', (): void => { it('Create API instance with metadata map and makes the runtime, rpc, state & extrinsics available', async (): Promise => { const rpcData = await provider.send('state_getMetadata', []); - const genesisHash = createType('Hash', await provider.send('chain_getBlockHash', [])).toHex(); + const genesisHash = createType(registry, 'Hash', await provider.send('chain_getBlockHash', [])).toHex(); const specVersion = 0; const metadata: any = {}; const key = `${genesisHash}-${specVersion}`; metadata[key] = rpcData; - const api = await ApiPromise.create({ provider, metadata } as unknown as ApiOptions); + const api = await ApiPromise.create({ provider, metadata, registry } as unknown as ApiOptions); expect(api.genesisHash).toBeDefined(); expect(api.runtimeMetadata).toBeDefined(); @@ -40,7 +41,7 @@ describe('ApiPromise', (): void => { it('Create API instance without metadata and makes the runtime, rpc, state & extrinsics available', async (): Promise => { const metadata = {}; - const api = await ApiPromise.create({ provider, metadata }); + const api = await ApiPromise.create({ provider, metadata, registry }); expect(api.genesisHash).toBeDefined(); expect(api.runtimeMetadata).toBeDefined(); @@ -58,7 +59,7 @@ describe('ApiPromise', (): void => { const SIG = '0x659effefbbe5ab4d7136ebb5084b959eb424e32b862307371be4721ac2c46334245af4f1476c36c5e5aff04396c2fdd2ce561ec90382821d4aa071b559b1db0f'; it('signs data using a specified keyring', async (): Promise => { - const api = new ApiPromise({ provider }); + const api = new ApiPromise({ provider, registry }); expect( await api.sign(keyring.alice_session, TEST) @@ -68,7 +69,8 @@ describe('ApiPromise', (): void => { it('signs data using an external signer', async (): Promise => { const api = new ApiPromise({ provider, - signer: new SingleAccountSigner(keyring.alice_session) + registry, + signer: new SingleAccountSigner(registry, keyring.alice_session) }); expect( diff --git a/packages/api/src/submittable/Submittable.ts b/packages/api/src/submittable/Submittable.ts deleted file mode 100644 index 7b86bc432e9a..000000000000 --- a/packages/api/src/submittable/Submittable.ts +++ /dev/null @@ -1,259 +0,0 @@ -/* eslint-disable no-dupe-class-members */ -// Copyright 2017-2019 @polkadot/api 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 { AccountId, Address, Call, Extrinsic, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index } from '@polkadot/types/interfaces'; -import { Callback, Codec, Constructor, IKeyringPair, SignatureOptions } from '@polkadot/types/types'; -import { ApiInterfaceRx, ApiTypes, SignerResult } from '../types'; -import { SignerOptions, SubmittableExtrinsic, SubmittableResultImpl, SubmittableResultResult, SubmittableResultSubscription } from './types'; - -import { Observable, combineLatest, of } from 'rxjs'; -import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators'; -import { createType, ClassOf } from '@polkadot/types'; -import { isBn, isFunction, isNumber, isUndefined } from '@polkadot/util'; - -import { filterEvents, isKeyringPair } from '../util'; -import ApiBase from '../base'; -import SubmittableResult from './Result'; - -interface SubmittableOptions { - api: ApiInterfaceRx; - decorateMethod: ApiBase['decorateMethod']; - type: ApiTypes; -} - -// The default for 6s allowing for 5min eras. When translating this to faster blocks - -// - 4s = (10 / 15) * 5 = 3.33m -// - 2s = (10 / 30) * 5 = 1.66m -const BLOCKTIME = 6; -const ONE_MINUTE = 60 / BLOCKTIME; -const DEFAULT_MORTAL_LENGTH = 5 * ONE_MINUTE; - -const _Extrinsic: Constructor = ClassOf('Extrinsic'); - -export default class Submittable extends _Extrinsic implements SubmittableExtrinsic { - private readonly _api: ApiInterfaceRx; - - private readonly _decorateMethod: ApiBase['decorateMethod']; - - private readonly _ignoreStatusCb: boolean; - - constructor (extrinsic: Call | Uint8Array | string, { api, decorateMethod, type }: SubmittableOptions) { - super(extrinsic, { version: api.extrinsicType }); - - this._api = api; - this._decorateMethod = decorateMethod; - this._ignoreStatusCb = type === 'rxjs'; - } - - // sign a transaction, returning the this to allow cianing, i.e. .sign(...).send() - public sign (account: IKeyringPair, optionsOrNonce: Partial): this { - // NOTE here we actually override nonce if it was specified (backwards compat for - // the previous signature - don't let userspace break, but allow then time to upgrade) - const options: Partial = isBn(optionsOrNonce) || isNumber(optionsOrNonce) - ? { nonce: optionsOrNonce } - : optionsOrNonce; - - super.sign(account, this._makeSignOptions(options, {})); - - return this; - } - - // signAndSend with an immediate Hash result - public signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial): SubmittableResultResult; - - // signAndSend with a subscription, i.e. callback provided - public signAndSend (account: IKeyringPair | string | AccountId | Address, statusCb: Callback): SubmittableResultSubscription; - - // signAndSend with options and a callback - public signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial, statusCb?: Callback): SubmittableResultSubscription; - - // signAndSend implementation for all 3 cases above - public signAndSend (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial | Callback, optionalStatusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { - const [options, statusCb] = this._makeSignAndSendOptions(optionsOrStatus, optionalStatusCb); - const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb); - const address = isKeyringPair(account) ? account.address : account.toString(); - let updateId: number | undefined; - - return this._decorateMethod( - (): Observable => ( - this._getPrelimState(address, options).pipe( - first(), - mergeMap(async ([nonce, header]): Promise => { - const eraOptions = this._makeEraOptions(options, { header, nonce }); - - if (isKeyringPair(account)) { - this.sign(account, eraOptions); - } else { - updateId = await this._signViaSigner(address, eraOptions, header); - } - }), - switchMap((): Observable | Observable => { - return isSubscription - ? this._subscribeObservable(updateId) - : this._sendObservable(updateId); - }) - ) as Observable) // FIXME This is wrong, SubmittableResult is _not_ a codec - )(statusCb) as SubmittableResultResult | SubmittableResultSubscription; - } - - // send with an immediate Hash result - public send (): SubmittableResultResult; - - // send with a status callback - public send (statusCb: Callback): SubmittableResultSubscription; - - // send implementation for both immediate Hash and statusCb variants - public send (statusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { - const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb); - - return this._decorateMethod( - isSubscription - ? this._subscribeObservable - : this._sendObservable - )(statusCb); - } - - private _makeSignAndSendOptions (optionsOrStatus?: Partial | Callback, statusCb?: Callback): [Partial, Callback?] { - let options: Partial = {}; - - if (isFunction(optionsOrStatus)) { - statusCb = optionsOrStatus; - } else { - options = { ...optionsOrStatus }; - } - - return [options, statusCb]; - } - - private async _signViaSigner (address: string, optionsWithEra: SignatureOptions, header: Header | null): Promise { - if (!this._api.signer) { - throw new Error('no signer attached'); - } - - const payload = createType('SignerPayload', { - ...optionsWithEra, - address, - method: this.method, - blockNumber: header ? header.number : 0 - }); - let result: SignerResult; - - if (this._api.signer.signPayload) { - result = await this._api.signer.signPayload(payload.toPayload()); - } else if (this._api.signer.signRaw) { - result = await this._api.signer.signRaw(payload.toRaw()); - } else { - throw new Error('Invalid signer interface, it should implement either signPayload or signRaw (or both)'); - } - - // Here we explicitly call `toPayload()` again instead of working with an object - // (reference) as passed to the signer. This means that we are sure that the - // payload data is not modified from our inputs, but the signer - super.addSignature(address, result.signature, payload.toPayload()); - - return result.id; - } - - private _makeSignOptions (options: Partial, extras: { blockHash?: Hash; era?: ExtrinsicEra; nonce?: Index }): SignatureOptions { - return { - blockHash: this._api.genesisHash, - ...options, - ...extras, - genesisHash: this._api.genesisHash, - runtimeVersion: this._api.runtimeVersion, - version: this._api.extrinsicType - } as unknown as SignatureOptions; - } - - private _makeEraOptions (options: Partial, { header, nonce }: { header: Header | null; nonce: Index }): SignatureOptions { - if (!header) { - if (isNumber(options.era)) { - // since we have no header, it is immortal, remove any option overrides - // so we only supply the genesisHash and no era to the construction - delete options.era; - delete options.blockHash; - } - - return this._makeSignOptions(options, { nonce }); - } - - return this._makeSignOptions(options, { - blockHash: header.hash, - era: createType('ExtrinsicEra', { - current: header.number, - period: options.era || DEFAULT_MORTAL_LENGTH - }), - nonce - }); - } - - private _getPrelimState (address: string, options: Partial): Observable<[Index, Header | null]> { - return combineLatest([ - // if we have a nonce already, don't retrieve the latest, use what is there - isUndefined(options.nonce) - // FIXME This apparently is having issues on latest Kusama for nonce retrieval, - // hence we are using the accountNonce only - // ? this._api.rpc.account.nextIndex - // ? this._api.rpc.account.nextIndex(address) - // : this._api.query.system.accountNonce(address) - ? this._api.query.system.accountNonce(address) - : 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)) - ? this._api.rpc.chain.getHeader() - : of(null) - ]); - } - - private _updateSigner (updateId: number, status: Hash | SubmittableResultImpl): void { - if ((updateId !== -1) && this._api.signer && this._api.signer.update) { - this._api.signer.update(updateId, status); - } - } - - private _statusObservable (status: ExtrinsicStatus): Observable { - if (!status.isFinalized) { - return of(new SubmittableResult({ status })); - } - - const blockHash = status.asFinalized; - - return combineLatest([ - this._api.rpc.chain.getBlock(blockHash), - this._api.query.system.events.at(blockHash) - ]).pipe( - map(([signedBlock, allEvents]): SubmittableResultImpl => - new SubmittableResult({ - events: filterEvents(this.hash, signedBlock, allEvents), - status - }) - ) - ); - } - - private _sendObservable = (updateId = -1): Observable => { - return this._api.rpc.author - .submitExtrinsic(this) - .pipe( - tap((hash): void => { - this._updateSigner(updateId, hash); - }) - ); - } - - private _subscribeObservable = (updateId = -1): Observable => { - return this._api.rpc.author - .submitAndWatchExtrinsic(this) - .pipe( - switchMap((status): Observable => - this._statusObservable(status) - ), - tap((status): void => { - this._updateSigner(updateId, status); - }) - ); - } -} diff --git a/packages/api/src/submittable/createClass.ts b/packages/api/src/submittable/createClass.ts new file mode 100644 index 000000000000..a4c6ab32798b --- /dev/null +++ b/packages/api/src/submittable/createClass.ts @@ -0,0 +1,262 @@ +/* eslint-disable no-dupe-class-members */ +// Copyright 2017-2019 @polkadot/api 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 { AccountId, Address, Call, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index } from '@polkadot/types/interfaces'; +import { Callback, Codec, Constructor, IKeyringPair, Registry, SignatureOptions } from '@polkadot/types/types'; +import { ApiInterfaceRx, ApiTypes, SignerResult } from '../types'; +import { SignerOptions, SubmittableExtrinsic, SubmittableResultImpl, SubmittableResultResult, SubmittableResultSubscription } from './types'; + +import { Observable, combineLatest, of } from 'rxjs'; +import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators'; +import { createType, ClassOf } from '@polkadot/types'; +import { isBn, isFunction, isNumber, isUndefined } from '@polkadot/util'; + +import { filterEvents, isKeyringPair } from '../util'; +import ApiBase from '../base'; +import SubmittableResult from './Result'; + +interface SubmittableOptions { + api: ApiInterfaceRx; + apiType: ApiTypes; + decorateMethod: ApiBase['decorateMethod']; +} + +// The default for 6s allowing for 5min eras. When translating this to faster blocks - +// - 4s = (10 / 15) * 5 = 3.33m +// - 2s = (10 / 30) * 5 = 1.66m +const BLOCKTIME = 6; +const ONE_MINUTE = 60 / BLOCKTIME; +const DEFAULT_MORTAL_LENGTH = 5 * ONE_MINUTE; + +export default function createClass ({ api, apiType, decorateMethod }: SubmittableOptions): Constructor> { + // an instance of the base extrinsic for us to extend + const Extrinsic = ClassOf(api.registry, 'Extrinsic'); + + return class Submittable extends Extrinsic implements SubmittableExtrinsic { + private readonly _api: ApiInterfaceRx; + + private readonly _decorateMethod: ApiBase['decorateMethod']; + + private readonly _ignoreStatusCb: boolean; + + constructor (registry: Registry, extrinsic: Call | Uint8Array | string) { + super(registry, extrinsic, { version: api.extrinsicType }); + + this._api = api; + this._decorateMethod = decorateMethod; + this._ignoreStatusCb = apiType === 'rxjs'; + } + + // sign a transaction, returning the this to allow cianing, i.e. .sign(...).send() + public sign (account: IKeyringPair, optionsOrNonce: Partial): this { + // NOTE here we actually override nonce if it was specified (backwards compat for + // the previous signature - don't let userspace break, but allow then time to upgrade) + const options: Partial = isBn(optionsOrNonce) || isNumber(optionsOrNonce) + ? { nonce: optionsOrNonce } + : optionsOrNonce; + + super.sign(account, this._makeSignOptions(options, {})); + + return this; + } + + // signAndSend with an immediate Hash result + public signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial): SubmittableResultResult; + + // signAndSend with a subscription, i.e. callback provided + public signAndSend (account: IKeyringPair | string | AccountId | Address, statusCb: Callback): SubmittableResultSubscription; + + // signAndSend with options and a callback + public signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial, statusCb?: Callback): SubmittableResultSubscription; + + // signAndSend implementation for all 3 cases above + public signAndSend (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial | Callback, optionalStatusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { + const [options, statusCb] = this._makeSignAndSendOptions(optionsOrStatus, optionalStatusCb); + const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb); + const address = isKeyringPair(account) ? account.address : account.toString(); + let updateId: number | undefined; + + return this._decorateMethod( + (): Observable => ( + this._getPrelimState(address, options).pipe( + first(), + mergeMap(async ([nonce, header]): Promise => { + const eraOptions = this._makeEraOptions(options, { header, nonce }); + + if (isKeyringPair(account)) { + this.sign(account, eraOptions); + } else { + updateId = await this._signViaSigner(address, eraOptions, header); + } + }), + switchMap((): Observable | Observable => { + return isSubscription + ? this._subscribeObservable(updateId) + : this._sendObservable(updateId); + }) + ) as Observable) // FIXME This is wrong, SubmittableResult is _not_ a codec + )(statusCb) as SubmittableResultResult | SubmittableResultSubscription; + } + + // send with an immediate Hash result + public send (): SubmittableResultResult; + + // send with a status callback + public send (statusCb: Callback): SubmittableResultSubscription; + + // send implementation for both immediate Hash and statusCb variants + public send (statusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { + const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb); + + return this._decorateMethod( + isSubscription + ? this._subscribeObservable + : this._sendObservable + )(statusCb); + } + + private _makeSignAndSendOptions (optionsOrStatus?: Partial | Callback, statusCb?: Callback): [Partial, Callback?] { + let options: Partial = {}; + + if (isFunction(optionsOrStatus)) { + statusCb = optionsOrStatus; + } else { + options = { ...optionsOrStatus }; + } + + return [options, statusCb]; + } + + private async _signViaSigner (address: string, optionsWithEra: SignatureOptions, header: Header | null): Promise { + if (!this._api.signer) { + throw new Error('no signer attached'); + } + + const payload = createType(this.registry, 'SignerPayload', { + ...optionsWithEra, + address, + method: this.method, + blockNumber: header ? header.number : 0 + }); + let result: SignerResult; + + if (this._api.signer.signPayload) { + result = await this._api.signer.signPayload(payload.toPayload()); + } else if (this._api.signer.signRaw) { + result = await this._api.signer.signRaw(payload.toRaw()); + } else { + throw new Error('Invalid signer interface, it should implement either signPayload or signRaw (or both)'); + } + + // Here we explicitly call `toPayload()` again instead of working with an object + // (reference) as passed to the signer. This means that we are sure that the + // payload data is not modified from our inputs, but the signer + super.addSignature(address, result.signature, payload.toPayload()); + + return result.id; + } + + private _makeSignOptions (options: Partial, extras: { blockHash?: Hash; era?: ExtrinsicEra; nonce?: Index }): SignatureOptions { + return { + blockHash: this._api.genesisHash, + ...options, + ...extras, + genesisHash: this._api.genesisHash, + runtimeVersion: this._api.runtimeVersion, + version: this._api.extrinsicType + } as unknown as SignatureOptions; + } + + private _makeEraOptions (options: Partial, { header, nonce }: { header: Header | null; nonce: Index }): SignatureOptions { + if (!header) { + if (isNumber(options.era)) { + // since we have no header, it is immortal, remove any option overrides + // so we only supply the genesisHash and no era to the construction + delete options.era; + delete options.blockHash; + } + + return this._makeSignOptions(options, { nonce }); + } + + return this._makeSignOptions(options, { + blockHash: header.hash, + era: createType(this.registry, 'ExtrinsicEra', { + current: header.number, + period: options.era || DEFAULT_MORTAL_LENGTH + }), + nonce + }); + } + + private _getPrelimState (address: string, options: Partial): Observable<[Index, Header | null]> { + return combineLatest([ + // if we have a nonce already, don't retrieve the latest, use what is there + isUndefined(options.nonce) + // FIXME This apparently is having issues on latest Kusama for nonce retrieval, + // hence we are using the accountNonce only + // ? this._api.rpc.account.nextIndex + // ? this._api.rpc.account.nextIndex(address) + // : this._api.query.system.accountNonce(address) + ? this._api.query.system.accountNonce(address) + : of(createType(this.registry, '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)) + ? this._api.rpc.chain.getHeader() + : of(null) + ]); + } + + private _updateSigner (updateId: number, status: Hash | SubmittableResultImpl): void { + if ((updateId !== -1) && this._api.signer && this._api.signer.update) { + this._api.signer.update(updateId, status); + } + } + + private _statusObservable (status: ExtrinsicStatus): Observable { + if (!status.isFinalized) { + return of(new SubmittableResult({ status })); + } + + const blockHash = status.asFinalized; + + return combineLatest([ + this._api.rpc.chain.getBlock(blockHash), + this._api.query.system.events.at(blockHash) + ]).pipe( + map(([signedBlock, allEvents]): SubmittableResultImpl => + new SubmittableResult({ + events: filterEvents(this.hash, signedBlock, allEvents), + status + }) + ) + ); + } + + private _sendObservable = (updateId = -1): Observable => { + return this._api.rpc.author + .submitExtrinsic(this) + .pipe( + tap((hash): void => { + this._updateSigner(updateId, hash); + }) + ); + } + + private _subscribeObservable = (updateId = -1): Observable => { + return this._api.rpc.author + .submitAndWatchExtrinsic(this) + .pipe( + switchMap((status): Observable => + this._statusObservable(status) + ), + tap((status): void => { + this._updateSigner(updateId, status); + }) + ); + } + }; +} diff --git a/packages/api/src/submittable/createSubmittable.ts b/packages/api/src/submittable/createSubmittable.ts index d473d4dd5172..2745fd1c39a8 100644 --- a/packages/api/src/submittable/createSubmittable.ts +++ b/packages/api/src/submittable/createSubmittable.ts @@ -8,19 +8,20 @@ import { ApiInterfaceRx, ApiTypes } from '../types'; import { SubmittableExtrinsic } from './types'; import ApiBase from '../base'; +import createClass from './createClass'; type Creator = (extrinsic: Call | Uint8Array | string) => SubmittableExtrinsic; let Submittable: Constructor>; -export default function createSubmittable (type: ApiTypes, api: ApiInterfaceRx, decorateMethod: ApiBase['decorateMethod']): Creator { +export default function createSubmittable (apiType: ApiTypes, api: ApiInterfaceRx, decorateMethod: ApiBase['decorateMethod']): Creator { return (extrinsic: Call | Uint8Array | string): SubmittableExtrinsic => { - // HACK This is not great, but basically what we do here is to lazily only require the class - // right at the point it is actually needed - delaying initialization + // This is not great, but basically what we do here is to only require the + // class right at the point it is actually needed - delaying initialization if (!Submittable) { - Submittable = require('./Submittable').default; + Submittable = createClass({ api, apiType, decorateMethod }); } - return new Submittable(extrinsic, { api, decorateMethod, type }); + return new Submittable(api.registry, extrinsic); }; } diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index e8016caaece9..c9fd26302afa 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -10,7 +10,7 @@ import './query.types'; import { Constants } from '@polkadot/metadata/Decorated/types'; import { UserRpc } from '@polkadot/rpc-core/types'; import { Hash, RuntimeVersion } from '@polkadot/types/interfaces'; -import { AnyFunction, Callback, CallFunction, Codec, CodecArg, RegistryTypes, SignatureOptions, SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; +import { AnyFunction, Callback, CallFunction, Codec, CodecArg, RegistryTypes, SignatureOptions, SignerPayloadJSON, SignerPayloadRaw, Registry } from '@polkadot/types/types'; import { SubmittableResultImpl, SubmittableExtrinsic } from './submittable/types'; import { DeriveAllSections } from './util/decorate'; @@ -192,6 +192,10 @@ export interface ApiOptions { * connecting to a WsProvider connecting localhost with the default port, i.e. `ws://127.0.0.1:9944` */ provider?: ProviderInterface; + /** + * @description A type registry to use along with this instance + */ + registry?: Registry; /** * @description User-defined RPC methods */ @@ -227,6 +231,7 @@ export interface ApiInterfaceRx { extrinsicType: number; genesisHash: Hash; hasSubscriptions: boolean; + registry: Registry; runtimeMetadata: Metadata; runtimeVersion: RuntimeVersion; query: QueryableStorage<'rxjs'>; diff --git a/packages/api/test/e2e/api-derive/rx.spec.ts b/packages/api/test/e2e/api-derive/rx.spec.ts index 2e8243409eb2..020c1c11f119 100644 --- a/packages/api/test/e2e/api-derive/rx.spec.ts +++ b/packages/api/test/e2e/api-derive/rx.spec.ts @@ -9,7 +9,7 @@ import BN from 'bn.js'; import ApiRx from '@polkadot/api/rx/Api'; import { HeaderExtended } from '@polkadot/api-derive'; import { DerivedBalances, DerivedContractFees, DerivedElectionsInfo, DerivedFees, DerivedSessionInfo } from '@polkadot/api-derive/types'; -import { createType, ClassOf } from '@polkadot/types'; +import { createType, ClassOf, TypeRegistry } from '@polkadot/types'; import { WsProvider } from '@polkadot/rpc-provider'; import { describeE2E } from '../../util'; @@ -24,6 +24,7 @@ describeE2E({ 'remote-substrate-1.0' ] })('Api-RX derive e2e', (wsUrl: string): void => { + const registry = new TypeRegistry(); let api: ApiRx; beforeAll((): void => { @@ -76,7 +77,7 @@ describeE2E({ api.derive.accounts.indexToId(IX).subscribe((accountId): void => { // The first emitted value for accountId is undefined when passing the IX if (accountId) { - expect(accountId instanceof ClassOf('AccountId')).toBe(true); + expect(accountId instanceof ClassOf(registry, 'AccountId')).toBe(true); expect(accountId.toString()).toEqual(ID); } else { expect(accountId).toEqual(undefined); @@ -91,7 +92,7 @@ describeE2E({ api.derive.accounts.idToIndex(ID).subscribe((accountIndex): void => { // The first emitted value for AccountIndex is undefined when passing the ID if (accountIndex) { - expect(accountIndex instanceof ClassOf('AccountIndex')).toBe(true); + expect(accountIndex instanceof ClassOf(registry, 'AccountIndex')).toBe(true); expect(accountIndex.toString()).toEqual(IX); } else { expect(accountIndex).toEqual(undefined); @@ -107,7 +108,7 @@ describeE2E({ // A local dev chain should have the AccountIndex of Alice expect(accountIndexes).toHaveProperty( ID, - createType('AccountIndex', IX) + createType(registry, 'AccountIndex', IX) ); done(); }); @@ -122,8 +123,8 @@ describeE2E({ it('It returns an object with all relevant balance information of an account', (done): void => { api.derive.balances.all(ID).subscribe((balances: DerivedBalances): void => { expect(balances).toEqual(expect.objectContaining({ - accountId: expect.any(ClassOf('AccountId')), - accountNonce: expect.any(ClassOf('Index')), + accountId: expect.any(ClassOf(registry, 'AccountId')), + accountNonce: expect.any(ClassOf(registry, 'Index')), availableBalance: expect.any(BN), freeBalance: expect.any(BN), lockedBalance: expect.any(BN), @@ -156,7 +157,7 @@ describeE2E({ describe('bestNumber', (): void => { it('Get the latest block number', (done): void => { api.derive.chain.bestNumber().subscribe((blockNumber: BlockNumber): void => { - expect(blockNumber instanceof ClassOf('BlockNumber')).toBe(true); + expect(blockNumber instanceof ClassOf(registry, 'BlockNumber')).toBe(true); expect(blockNumber.gten(0)).toBe(true); done(); }); @@ -166,7 +167,7 @@ describeE2E({ describe('bestNumberFinalized', (): void => { it('Get the latest finalised block number', (done): void => { api.derive.chain.bestNumberFinalized().subscribe((blockNumber: BlockNumber): void => { - expect(blockNumber instanceof ClassOf('BlockNumber')).toBe(true); + expect(blockNumber instanceof ClassOf(registry, 'BlockNumber')).toBe(true); expect(blockNumber.gten(0)).toBe(true); done(); }); @@ -176,7 +177,7 @@ describeE2E({ describe('bestNumberLag', (): void => { it('lag between finalised head and best head', (done): void => { api.derive.chain.bestNumberLag().subscribe((numberLag: BlockNumber): void => { - expect(numberLag instanceof ClassOf('BlockNumber')).toBe(true); + expect(numberLag instanceof ClassOf(registry, 'BlockNumber')).toBe(true); expect(numberLag.gten(0)).toBe(true); done(); }); diff --git a/packages/api/test/e2e/api/promise-alex.spec.ts b/packages/api/test/e2e/api/promise-alex.spec.ts index 66b18a40ab49..4f9133f4019c 100644 --- a/packages/api/test/e2e/api/promise-alex.spec.ts +++ b/packages/api/test/e2e/api/promise-alex.spec.ts @@ -5,7 +5,7 @@ import { AccountId, EventRecord } from '@polkadot/types/interfaces'; import WsProvider from '@polkadot/rpc-provider/ws'; -import { ClassOf, Option, Vec } from '@polkadot/types'; +import { ClassOf, Option, TypeRegistry, Vec } from '@polkadot/types'; import ApiPromise from '../../../src/promise'; import { describeE2E } from '../../util'; @@ -13,6 +13,7 @@ import { describeE2E } from '../../util'; describeE2E({ only: ['docker-polkadot-alexander', 'remote-polkadot-alexander'] })('Promise e2e alex queries', (wsUrl: string): void => { + const registry = new TypeRegistry(); let api: ApiPromise; beforeEach(async (done): Promise => { @@ -46,7 +47,7 @@ describeE2E({ it('Gets the hash of the last finalized header', async (done): Promise<() => void> => { return ( api.rpc.chain.getFinalizedHead((head): void => { - expect(head instanceof ClassOf('Hash')).toBe(true); + expect(head instanceof ClassOf(registry, 'Hash')).toBe(true); done(); }) ); @@ -55,7 +56,7 @@ describeE2E({ it('Subscribes to the best finalized header on ALEX', async (done): Promise<() => void> => { return ( api.rpc.chain.subscribeFinalizedHeads((head): void => { - expect(head instanceof ClassOf('Header')).toBe(true); + expect(head instanceof ClassOf(registry, 'Header')).toBe(true); done(); }) ); diff --git a/packages/api/test/e2e/api/promise-consts.spec.ts b/packages/api/test/e2e/api/promise-consts.spec.ts index 8556e3700110..7362cf21c4d1 100644 --- a/packages/api/test/e2e/api/promise-consts.spec.ts +++ b/packages/api/test/e2e/api/promise-consts.spec.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import WsProvider from '@polkadot/rpc-provider/ws'; -import { ClassOf } from '@polkadot/types'; +import { ClassOf, TypeRegistry } from '@polkadot/types'; import ApiPromise from '../../../src/promise'; import { describeE2E } from '../../util'; @@ -16,6 +16,7 @@ describeE2E({ 'docker-substrate-2.0' ] })('Promise e2e consts', (wsUrl: string): void => { + const registry = new TypeRegistry(); let api: ApiPromise; beforeEach(async (done): Promise => { @@ -25,7 +26,7 @@ describeE2E({ }); it('democracy.cooloffPeriod parameter type', (): void => { - expect(api.consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf('BlockNumber')); + expect(api.consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf(registry, 'BlockNumber')); expect( api.consts.democracy.cooloffPeriod.eq(432000) || // Substrate api.consts.democracy.cooloffPeriod.eq(259200) // Polkadot diff --git a/packages/api/test/e2e/api/promise-contract.spec.ts b/packages/api/test/e2e/api/promise-contract.spec.ts index 82a5169da1da..5855205877f0 100644 --- a/packages/api/test/e2e/api/promise-contract.spec.ts +++ b/packages/api/test/e2e/api/promise-contract.spec.ts @@ -48,7 +48,7 @@ describeE2E({ let abi: Abi; beforeEach((): void => { - abi = new Abi(incrementerAbi); + abi = new Abi(api.registry, incrementerAbi); }); it('allows putCode', (done): Promise<() => void> => { diff --git a/packages/api/test/e2e/api/promise-queries-dev.spec.ts b/packages/api/test/e2e/api/promise-queries-dev.spec.ts index 02a869fd51e6..41cad6a21a52 100644 --- a/packages/api/test/e2e/api/promise-queries-dev.spec.ts +++ b/packages/api/test/e2e/api/promise-queries-dev.spec.ts @@ -9,7 +9,7 @@ import BN from 'bn.js'; import { DerivedBalances } from '@polkadot/api-derive/types'; import testingPairs from '@polkadot/keyring/testingPairs'; import WsProvider from '@polkadot/rpc-provider/ws'; -import { Option, u32, createType } from '@polkadot/types'; +import { Option, u32, createType, TypeRegistry } from '@polkadot/types'; import ApiPromise from '../../../src/promise'; import { describeE2E } from '../../util'; @@ -23,6 +23,7 @@ describeE2E({ 'remote-substrate-1.0' ] })('Promise e2e development queries', (wsUrl: string): void => { + const registry = new TypeRegistry(); let api: ApiPromise; beforeEach(async (done): Promise => { @@ -159,7 +160,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; - const balanceRPC = createType('Balance', balanceData.unwrapOr(undefined)); + const balanceRPC = createType(registry, 'Balance', balanceData.unwrapOr(undefined)); const balance = await api.query.balances.freeBalance(keyring.alice_stash.address); diff --git a/packages/api/test/e2e/api/promise-queries.spec.ts b/packages/api/test/e2e/api/promise-queries.spec.ts index bdad8979f304..bc7027f4a168 100644 --- a/packages/api/test/e2e/api/promise-queries.spec.ts +++ b/packages/api/test/e2e/api/promise-queries.spec.ts @@ -7,7 +7,7 @@ import { EventRecord, Hash, Header, SessionIndex } from '@polkadot/types/interfa import { HeaderExtended } from '@polkadot/api-derive'; import WsProvider from '@polkadot/rpc-provider/ws'; import { LinkageResult } from '@polkadot/types/codec/Linkage'; -import { Option, createType } from '@polkadot/types'; +import { Option } from '@polkadot/types'; import ApiPromise from '../../../src/promise'; import { describeE2E } from '../../util'; @@ -128,7 +128,7 @@ describeE2E()('Promise e2e queries', (wsUrl: string): void => { it('gets correct key', async (): Promise => { const key = api.query.session.currentIndex.key(); const sessionIndexData = await api.rpc.state.getStorage>(key); - const sessionIndexRPC = createType('SessionIndex', sessionIndexData.unwrapOr(undefined)); + const sessionIndexRPC = api.createType('SessionIndex', sessionIndexData.unwrapOr(undefined)); expect(sessionIndexRPC.toNumber()).toBeGreaterThanOrEqual(0); }); diff --git a/packages/api/test/e2e/api/promise-tx-eras.spec.ts b/packages/api/test/e2e/api/promise-tx-eras.spec.ts index 104db3dcd7a1..4015ba3ce446 100644 --- a/packages/api/test/e2e/api/promise-tx-eras.spec.ts +++ b/packages/api/test/e2e/api/promise-tx-eras.spec.ts @@ -5,7 +5,7 @@ import testingPairs from '@polkadot/keyring/testingPairs'; import WsProvider from '@polkadot/rpc-provider/ws'; import { Header } from '@polkadot/types/interfaces'; -import { createType } from '@polkadot/types'; +import { createType, TypeRegistry } from '@polkadot/types'; import ApiPromise from '../../../src/promise'; import { Signer } from '../../../src/types'; @@ -19,6 +19,7 @@ describeE2E({ 'docker-substrate-1.0' ] })('Promise e2e transactions with specified eras', (wsUrl: string): void => { + const registry = new TypeRegistry(); const keyring = testingPairs({ type: 'ed25519' }); let api: ApiPromise; @@ -32,7 +33,7 @@ describeE2E({ it('makes a transfer (specified era)', async (done): Promise => { const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = createType('ExtrinsicEra', { current: currentHeight, period: 4 }); + const exERA = createType(registry, 'ExtrinsicEra', { current: currentHeight, period: 4 }); const ex = api.tx.balances.transfer(keyring.eve.address, 12345); await ex.signAndSend(keyring.charlie, { @@ -44,7 +45,7 @@ describeE2E({ it('makes a transfer (specified era, previous block)', async (done): Promise => { const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number.toBn().subn(1); - const exERA = createType('ExtrinsicEra', { current: currentHeight, period: 10 }); + const exERA = createType(registry, 'ExtrinsicEra', { current: currentHeight, period: 10 }); const ex = api.tx.balances.transfer(keyring.eve.address, 12345); await ex.signAndSend(keyring.charlie, { @@ -57,7 +58,7 @@ describeE2E({ const nonce = await api.query.system.accountNonce(keyring.alice.address); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = createType('ExtrinsicEra', { current: currentHeight, period: 4 }); + const exERA = createType(registry, 'ExtrinsicEra', { current: currentHeight, period: 4 }); const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber()); const blockHash = signedBlock.block.header.hash; const ex = api.tx.balances.transfer(keyring.eve.address, 12345); @@ -79,14 +80,14 @@ describeE2E({ }); it('fails on a transfer with invalid time (via Signer)', async (done): Promise => { - const signer: Signer = new SingleAccountSigner(keyring.alice); + const signer: Signer = new SingleAccountSigner(registry, keyring.alice); api.setSigner(signer); const nonce = await api.query.system.accountNonce(keyring.bob_stash.address); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = createType('ExtrinsicEra', { current: currentHeight, period: 4 }); + const exERA = createType(registry, 'ExtrinsicEra', { current: currentHeight, period: 4 }); const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber()); const blockHash = signedBlock.block.header.hash; const ex = api.tx.balances.transfer(keyring.eve.address, 12121); diff --git a/packages/api/test/e2e/api/promise-tx-signer.spec.ts b/packages/api/test/e2e/api/promise-tx-signer.spec.ts index 80081d1ad175..48660682a7b3 100644 --- a/packages/api/test/e2e/api/promise-tx-signer.spec.ts +++ b/packages/api/test/e2e/api/promise-tx-signer.spec.ts @@ -4,6 +4,7 @@ import testingPairs from '@polkadot/keyring/testingPairs'; import WsProvider from '@polkadot/rpc-provider/ws'; +import { TypeRegistry } from '@polkadot/types'; import ApiPromise from '../../../src/promise'; import { Signer } from '../../../src/types'; @@ -12,6 +13,7 @@ import { describeE2E, logEvents, SingleAccountSigner } from '../../util'; describeE2E({ except: ['remote-polkadot-alexander', 'remote-substrate-1.0'] })('Promise e2e transactions with Signer injection', (wsUrl: string): void => { + const registry = new TypeRegistry(); const keyring = testingPairs({ type: 'ed25519' }); let api: ApiPromise; @@ -23,7 +25,7 @@ describeE2E({ describe('Signer injection', (): void => { it('makes a transfer (signAndSend via Signer)', async (done): Promise => { - const signer = new SingleAccountSigner(keyring.bob_stash); + const signer = new SingleAccountSigner(registry, keyring.bob_stash); api.setSigner(signer); @@ -34,7 +36,7 @@ describeE2E({ it('succeeds when waiting some blocks before submission', async (done): Promise => { // 10 second delay - const signer = new SingleAccountSigner(keyring.bob_stash, 10000); + const signer = new SingleAccountSigner(registry, keyring.bob_stash, 10000); api.setSigner(signer); @@ -54,7 +56,7 @@ describeE2E({ }); it('fails (signAndSend via Signer) with the wrong keyring pair', async (): Promise => { - const signer: Signer = new SingleAccountSigner(keyring.dave); + const signer: Signer = new SingleAccountSigner(registry, keyring.dave); api.setSigner(signer); diff --git a/packages/api/test/e2e/api/promise-tx.spec.ts b/packages/api/test/e2e/api/promise-tx.spec.ts index f2fddab8300a..9bbdeaeda29b 100644 --- a/packages/api/test/e2e/api/promise-tx.spec.ts +++ b/packages/api/test/e2e/api/promise-tx.spec.ts @@ -2,7 +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 '@polkadot/types/injector'; import testingPairs from '@polkadot/keyring/testingPairs'; import WsProvider from '@polkadot/rpc-provider/ws'; import { randomAsHex } from '@polkadot/util-crypto'; diff --git a/packages/api/test/e2e/rpc-core/alexander.spec.ts b/packages/api/test/e2e/rpc-core/alexander.spec.ts index 7bce362677f3..65446bbe1203 100644 --- a/packages/api/test/e2e/rpc-core/alexander.spec.ts +++ b/packages/api/test/e2e/rpc-core/alexander.spec.ts @@ -7,14 +7,13 @@ import { BlockNumber, Index, SignedBlock } from '@polkadot/types/interfaces'; import Metadata from '@polkadot/metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import WsProvider from '@polkadot/rpc-provider/ws'; -import { ClassOf } from '@polkadot/types'; +import { ClassOf, TypeRegistry } from '@polkadot/types'; import Rpc from '@polkadot/rpc-core'; import { describeE2E } from '../../util'; const randomAccount = '5HTqyWJHAVUieZnpb1V8gK4T1E4mnhkrUVSSzWBQd6kYgsVJ'; -const metadata = new Metadata(rpcMetadata); describeE2E({ only: [ @@ -22,10 +21,12 @@ describeE2E({ 'docker-polkadot-alexander' ] })('e2e Alexander - Polkadot', (wsUrl: string): void => { + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, rpcMetadata); let rpc: Rpc; beforeEach((): void => { - rpc = new Rpc(new WsProvider(wsUrl)); + rpc = new Rpc(registry, new WsProvider(wsUrl)); }); it('subscribes to storage', (done): void => { @@ -38,8 +39,8 @@ describeE2E({ expect(data).toHaveLength(2); expect(data).toEqual( expect.arrayContaining([ - expect.any(ClassOf('Index')), - expect.any(ClassOf('Index')) + expect.any(ClassOf(registry, 'Index')), + expect.any(ClassOf(registry, 'Index')) ]) ); @@ -52,7 +53,7 @@ describeE2E({ .getBlock('0x627847bffdf5f3e01ac440d057dec6a37a12a6f329db7ef8367665574b76b5df') .subscribe((block: SignedBlock): void => { expect(block).toBeDefined(); - expect(block).toBeInstanceOf(ClassOf('SignedBlock')); + expect(block).toBeInstanceOf(ClassOf(registry, 'SignedBlock')); done(); }); }); @@ -62,7 +63,7 @@ describeE2E({ .getBlock('0x53416d53a4b1dfcae9165a89d193608e4aa770414f02267f5b2c4015a2e66091') .subscribe((block: SignedBlock): void => { expect(block).toBeDefined(); - expect(block).toBeInstanceOf(ClassOf('SignedBlock')); + expect(block).toBeInstanceOf(ClassOf(registry, 'SignedBlock')); done(); }); }); diff --git a/packages/api/test/e2e/rpc-core/basics.spec.ts b/packages/api/test/e2e/rpc-core/basics.spec.ts index dddad1f202a3..ba080f8ee20b 100644 --- a/packages/api/test/e2e/rpc-core/basics.spec.ts +++ b/packages/api/test/e2e/rpc-core/basics.spec.ts @@ -4,7 +4,7 @@ import { ChainProperties, Extrinsic } from '@polkadot/types/interfaces'; -import { ClassOf, Vec } from '@polkadot/types'; +import { ClassOf, TypeRegistry, Vec } from '@polkadot/types'; import WsProvider from '@polkadot/rpc-provider/ws'; import Rpc from '@polkadot/rpc-core'; @@ -16,11 +16,12 @@ describeE2E({ 'remote-substrate-1.0' ] })('RPC-core e2e basics', (wsUrl: string): void => { + const registry = new TypeRegistry(); let rpc: Rpc; beforeEach((): void => { jest.setTimeout(30000); - rpc = new Rpc(new WsProvider(wsUrl)); + rpc = new Rpc(registry, new WsProvider(wsUrl)); }); it('retrieves the pending extrinsics', (done): void => { @@ -36,7 +37,7 @@ describeE2E({ rpc.system .properties() .subscribe((properties: ChainProperties): void => { - expect(properties).toBeInstanceOf(ClassOf('ChainProperties')); + expect(properties).toBeInstanceOf(ClassOf(registry, 'ChainProperties')); done(); }); }); diff --git a/packages/api/test/e2e/rpc-core/chain.spec.ts b/packages/api/test/e2e/rpc-core/chain.spec.ts index a8159b7420c2..099a425c069b 100644 --- a/packages/api/test/e2e/rpc-core/chain.spec.ts +++ b/packages/api/test/e2e/rpc-core/chain.spec.ts @@ -4,7 +4,7 @@ import { Header } from '@polkadot/types/interfaces'; -import { ClassOf } from '@polkadot/types'; +import { ClassOf, TypeRegistry } from '@polkadot/types'; import WsProvider from '@polkadot/rpc-provider/ws'; import Rpc from '@polkadot/rpc-core'; @@ -16,11 +16,12 @@ describeE2E({ 'remote-substrate-1.0' ] })('RPC-core e2e chain', (wsUrl: string): void => { + const registry = new TypeRegistry(); let rpc: Rpc; beforeEach((): void => { jest.setTimeout(30000); - rpc = new Rpc(new WsProvider(wsUrl)); + rpc = new Rpc(registry, new WsProvider(wsUrl)); }); it('subscribes via subscribeNewHeads', (done): void => { @@ -29,7 +30,7 @@ describeE2E({ rpc.chain .subscribeNewHeads() .subscribe((header: Header): void => { - expect(header).toBeInstanceOf(ClassOf('Header')); + expect(header).toBeInstanceOf(ClassOf(registry, 'Header')); if (++count === 3) { done(); diff --git a/packages/api/test/e2e/rpc-core/child-storage.spec.ts b/packages/api/test/e2e/rpc-core/child-storage.spec.ts index 4db6f5008bd8..321973f20cb6 100644 --- a/packages/api/test/e2e/rpc-core/child-storage.spec.ts +++ b/packages/api/test/e2e/rpc-core/child-storage.spec.ts @@ -2,11 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Hash } from '@polkadot/types/interfaces'; + import BN from 'bn.js'; import fs from 'fs'; import path from 'path'; -import { Hash } from '@polkadot/types/interfaces'; -import { H256, StorageData, StorageKey } from '@polkadot/types'; import { ApiPromise, SubmittableResult } from '@polkadot/api'; import { Abi } from '@polkadot/api-contract'; @@ -14,6 +14,7 @@ import testingPairs from '@polkadot/keyring/testingPairs'; import { KeyringPair } from '@polkadot/keyring/types'; import Rpc from '@polkadot/rpc-core'; import WsProvider from '@polkadot/rpc-provider/ws'; +import { H256, StorageData, StorageKey, TypeRegistry } from '@polkadot/types'; import { hexToBn, isInstanceOf } from '@polkadot/util'; import { describeE2E } from '../../util'; @@ -37,6 +38,7 @@ describeE2E({ ] })('RPC-core e2e child-storage', (wsUrl: string): void => { const MAX_GAS = 50000; + const registry = new TypeRegistry(); const keyring: Record = testingPairs({ type: 'sr25519' }); const randomStart = Math.floor(Date.now() / 1000); let abi: Abi; @@ -45,7 +47,7 @@ describeE2E({ let rpc: Rpc; beforeAll(async (done): Promise<() => void> => { - abi = new Abi(incrementerAbi); + abi = new Abi(registry, incrementerAbi); api = await ApiPromise.create({ provider: new WsProvider(wsUrl) }); return ( @@ -64,7 +66,7 @@ describeE2E({ }); beforeEach((done): void => { - rpc = new Rpc(new WsProvider(wsUrl)); + rpc = new Rpc(registry, new WsProvider(wsUrl)); done(); }); @@ -73,7 +75,7 @@ describeE2E({ // @TODO Add tests for Polkadot once child storage is being used there. describe('e2e state child methods', (): void => { beforeAll(async (done): Promise<() => void> => { - abi = new Abi(incrementerAbi); + abi = new Abi(registry, incrementerAbi); api = await ApiPromise.create({ provider: new WsProvider(wsUrl) }); // An instance of a contract can only be deployed once by one specific account. // That's why we need a random starting point for our incrementer contract to be diff --git a/packages/api/test/e2e/rpc-core/state.spec.ts b/packages/api/test/e2e/rpc-core/state.spec.ts index a392d2f6b09d..1dc7340a8ebd 100644 --- a/packages/api/test/e2e/rpc-core/state.spec.ts +++ b/packages/api/test/e2e/rpc-core/state.spec.ts @@ -8,13 +8,15 @@ import DecorateMeta from '@polkadot/metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import Rpc from '@polkadot/rpc-core'; import WsProvider from '@polkadot/rpc-provider/ws'; -import { Bytes, ClassOf, Metadata, StorageKey } from '@polkadot/types'; +import { Bytes, ClassOf, Metadata, StorageKey, TypeRegistry } from '@polkadot/types'; import { describeE2E } from '../../util'; const BOB_STASH = '5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc'; const CODE = '0x3a636f6465'; // :code -const metadata = new DecorateMeta(rpcMetadata); + +const registry = new TypeRegistry(); +const metadata = new DecorateMeta(registry, rpcMetadata); describeE2E({ except: [ @@ -26,7 +28,7 @@ describeE2E({ beforeEach((): void => { jest.setTimeout(30000); - rpc = new Rpc(new WsProvider(wsUrl)); + rpc = new Rpc(registry, new WsProvider(wsUrl)); }); it('getMetadata(): retrieves the wasm metadata', (done): void => { @@ -52,7 +54,7 @@ describeE2E({ rpc.state .getRuntimeVersion() .subscribe((version: RuntimeVersion): void => { - expect(version).toBeInstanceOf(ClassOf('RuntimeVersion')); + expect(version).toBeInstanceOf(ClassOf(registry, 'RuntimeVersion')); done(); }); }); diff --git a/packages/api/test/e2e/rpc-core/subscribe.spec.ts b/packages/api/test/e2e/rpc-core/subscribe.spec.ts index 5a88420b0861..3629adbcb513 100644 --- a/packages/api/test/e2e/rpc-core/subscribe.spec.ts +++ b/packages/api/test/e2e/rpc-core/subscribe.spec.ts @@ -8,11 +8,12 @@ import testingPairs from '@polkadot/keyring/testingPairs'; import WsProvider from '@polkadot/rpc-provider/ws'; import Rpc from '@polkadot/rpc-core'; import { Index, Moment, SessionIndex } from '@polkadot/types/interfaces'; -import { ClassOf } from '@polkadot/types'; +import { ClassOf, TypeRegistry } from '@polkadot/types'; import { describeE2E } from '../../util'; -const metadata = new Metadata(rpcMetadata); +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); describeE2E({ except: [ @@ -25,7 +26,7 @@ describeE2E({ beforeEach((): void => { jest.setTimeout(30000); - rpc = new Rpc(new WsProvider(wsUrl)); + rpc = new Rpc(registry, new WsProvider(wsUrl)); }); it('subscribes to storage', (done): void => { @@ -38,8 +39,8 @@ describeE2E({ expect(data).toHaveLength(2); expect(data).toEqual( expect.arrayContaining([ - expect.any(ClassOf('Index')), - expect.any(ClassOf('Index')) + expect.any(ClassOf(registry, 'Index')), + expect.any(ClassOf(registry, 'Index')) ]) ); diff --git a/packages/api/test/util/SingleAccountSigner.ts b/packages/api/test/util/SingleAccountSigner.ts index 4b0734973143..58327dd369cc 100644 --- a/packages/api/test/util/SingleAccountSigner.ts +++ b/packages/api/test/util/SingleAccountSigner.ts @@ -4,20 +4,23 @@ import { Signer, SignerResult } from '@polkadot/api/types'; import { KeyringPair } from '@polkadot/keyring/types'; +import { Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import { createType } from '@polkadot/types'; import { hexToU8a, u8aToHex } from '@polkadot/util'; -import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; let id = 0; export class SingleAccountSigner implements Signer { private keyringPair: KeyringPair; + private registry: Registry; + private signDelay: number; - constructor (keyringPair: KeyringPair, signDelay = 0) { + constructor (registry: Registry, keyringPair: KeyringPair, signDelay = 0) { this.keyringPair = keyringPair; + this.registry = registry; this.signDelay = signDelay; } @@ -28,7 +31,7 @@ export class SingleAccountSigner implements Signer { return new Promise((resolve): void => { setTimeout((): void => { - const signed = createType('ExtrinsicPayload', payload, { version: payload.version }).sign(this.keyringPair); + const signed = createType(this.registry, 'ExtrinsicPayload', payload, { version: payload.version }).sign(this.keyringPair); resolve({ id: ++id, diff --git a/packages/metadata/src/Decorated/Decorated.spec.ts b/packages/metadata/src/Decorated/Decorated.spec.ts index 194e715e5e2d..9c1604ce8e25 100644 --- a/packages/metadata/src/Decorated/Decorated.spec.ts +++ b/packages/metadata/src/Decorated/Decorated.spec.ts @@ -2,14 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '@polkadot/types/injector'; - -import { ClassOf, createType } from '@polkadot/types/codec/create'; +import { ClassOf, createType, TypeRegistry } from '@polkadot/types/codec/create'; import Decorated from './Decorated'; import json from '../Metadata/static'; -const decorated = new Decorated(json); +const registry = new TypeRegistry(); +const decorated = new Decorated(registry, json); describe('Decorated', () => { it('should correctly get Alice\'s freeBalance storage key (u8a)', (): void => { @@ -27,7 +26,7 @@ describe('Decorated', () => { it('should return properly-encoded transactions', (): void => { expect( - createType('Extrinsic', decorated.tx.timestamp.set([10101])).toU8a() + createType(registry, 'Extrinsic', decorated.tx.timestamp.set([10101])).toU8a() ).toEqual( new Uint8Array([ // length (encoded) @@ -43,7 +42,7 @@ describe('Decorated', () => { }); it('should return constants with the correct type and value', (): void => { - expect(decorated.consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf('BlockNumber')); + expect(decorated.consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf(registry, 'BlockNumber')); expect(decorated.consts.democracy.cooloffPeriod.toHex()).toEqual('0x000c4e00'); }); }); diff --git a/packages/metadata/src/Decorated/Decorated.ts b/packages/metadata/src/Decorated/Decorated.ts index 3c546b39fed1..dd643f6d47d7 100644 --- a/packages/metadata/src/Decorated/Decorated.ts +++ b/packages/metadata/src/Decorated/Decorated.ts @@ -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 { ModulesWithCalls } from '@polkadot/types/types'; +import { ModulesWithCalls, Registry } from '@polkadot/types/types'; import { Constants, Storage } from './types'; import Metadata from '../Metadata'; @@ -20,14 +20,19 @@ export default class Decorated { public readonly metadata: Metadata; + public readonly registry: Registry; + public readonly query: Storage; public readonly tx: ModulesWithCalls; - constructor (value?: Uint8Array | string | Metadata) { - this.metadata = value instanceof Metadata ? value : new Metadata(value); - this.tx = extrinsicsFromMeta(this.metadata); - this.query = storageFromMeta(this.metadata); - this.consts = constantsFromMeta(this.metadata); + constructor (registry: Registry, value?: Uint8Array | string | Metadata) { + this.registry = registry; + this.metadata = value instanceof Metadata ? value : new Metadata(registry, value); + + // decoration + this.tx = extrinsicsFromMeta(registry, this.metadata); + this.query = storageFromMeta(registry, this.metadata); + this.consts = constantsFromMeta(registry, this.metadata); } } diff --git a/packages/metadata/src/Decorated/consts/fromMetadata/fromMetadata.spec.ts b/packages/metadata/src/Decorated/consts/fromMetadata/fromMetadata.spec.ts index 3649cdab7613..0acaf5af2216 100644 --- a/packages/metadata/src/Decorated/consts/fromMetadata/fromMetadata.spec.ts +++ b/packages/metadata/src/Decorated/consts/fromMetadata/fromMetadata.spec.ts @@ -2,20 +2,19 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '@polkadot/types/injector'; - -import { ClassOf, Metadata } from '@polkadot/types'; +import { ClassOf, Metadata, TypeRegistry } from '@polkadot/types'; import rpcMetadata from '../../../Metadata/static'; import fromMetadata from '../fromMetadata'; // Use the pre-generated metadata -const metadata = new Metadata(rpcMetadata); -const consts = fromMetadata(metadata); +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); +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('BlockNumber')); + expect(consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf(registry, 'BlockNumber')); expect(consts.democracy.cooloffPeriod.toHex()).toEqual('0x000c4e00'); }); }); diff --git a/packages/metadata/src/Decorated/consts/fromMetadata/index.ts b/packages/metadata/src/Decorated/consts/fromMetadata/index.ts index 81054e7d5ced..e78df204194c 100644 --- a/packages/metadata/src/Decorated/consts/fromMetadata/index.ts +++ b/packages/metadata/src/Decorated/consts/fromMetadata/index.ts @@ -2,6 +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 { Registry } from '@polkadot/types/types'; import { Constants, ConstantCodec, ModuleConstants } from '../../types'; import { createTypeUnsafe } from '@polkadot/types/codec'; @@ -14,7 +15,7 @@ import Metadata from '../../../Metadata'; * * @param metadata - The metadata */ -export default function fromMetadata (metadata: Metadata): Constants { +export default function fromMetadata (registry: Registry, metadata: Metadata): Constants { return metadata.asLatest.modules.reduce((result: Constants, moduleMetadata): Constants => { if (moduleMetadata.constants.isEmpty) { return result; @@ -24,7 +25,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: ModuleConstants, meta): ModuleConstants => { - const codec = createTypeUnsafe(meta.type.toString(), [meta.value]); + const codec = createTypeUnsafe(registry, 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 diff --git a/packages/metadata/src/Decorated/extrinsics/fromMetadata/createUnchecked.ts b/packages/metadata/src/Decorated/extrinsics/fromMetadata/createUnchecked.ts index 7d0625fe0ffe..6b2b8b916a66 100644 --- a/packages/metadata/src/Decorated/extrinsics/fromMetadata/createUnchecked.ts +++ b/packages/metadata/src/Decorated/extrinsics/fromMetadata/createUnchecked.ts @@ -2,10 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Call, FunctionMetadataLatest } from '@polkadot/types/interfaces'; -import { CallFunction } from '@polkadot/types/types'; +import { Call } from '@polkadot/types/interfaces'; +import { AnyJsonObject, CallFunction, Registry, RegistryMetadataCall } from '@polkadot/types/types'; -import { createType } from '@polkadot/types/codec'; +// we do a direct import here to remove all circular dependencies (importing from +// /types, /types/codec or /types/codec/create makes all hell break loose) +import { createType } from '@polkadot/types/codec/create/createType'; import { assert, stringCamelCase } from '@polkadot/util'; /** @@ -17,12 +19,7 @@ import { assert, stringCamelCase } from '@polkadot/util'; * @param methodIndex - Index of the method inside the section. * @param callMetadata - Metadata of the call function. */ -export default function createDescriptor ( - section: string, - sectionIndex: number, - methodIndex: number, - callMetadata: FunctionMetadataLatest -): CallFunction { +export default function createDescriptor (registry: Registry, section: string, sectionIndex: number, methodIndex: number, callMetadata: RegistryMetadataCall): CallFunction { const callIndex = new Uint8Array([sectionIndex, methodIndex]); const expectedArgs = callMetadata.args; const funcName = stringCamelCase(callMetadata.name.toString()); @@ -32,7 +29,7 @@ export default function createDescriptor ( `Extrinsic ${section}.${funcName} expects ${expectedArgs.length.valueOf()} arguments, got ${args.length}.` ); - return createType('Call', { + return createType(registry, 'Call', { args, callIndex }, callMetadata); @@ -42,7 +39,7 @@ export default function createDescriptor ( extrinsicFn.meta = callMetadata; extrinsicFn.method = funcName; extrinsicFn.section = section; - extrinsicFn.toJSON = (): any => + extrinsicFn.toJSON = (): string | AnyJsonObject => callMetadata.toJSON(); return extrinsicFn as CallFunction; diff --git a/packages/metadata/src/Decorated/extrinsics/fromMetadata/fromMetadata.spec.ts b/packages/metadata/src/Decorated/extrinsics/fromMetadata/fromMetadata.spec.ts index 833bbada89ff..9deaaf5abd24 100644 --- a/packages/metadata/src/Decorated/extrinsics/fromMetadata/fromMetadata.spec.ts +++ b/packages/metadata/src/Decorated/extrinsics/fromMetadata/fromMetadata.spec.ts @@ -2,16 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '@polkadot/types/injector'; - -import { createType, Metadata } from '@polkadot/types'; +import { createType, Metadata, TypeRegistry } from '@polkadot/types'; import json from '../../../Metadata/static'; import fromMetadata from '.'; // Use the pre-generated metadata -const metadata = new Metadata(json); -const newExtrinsics = fromMetadata(metadata); +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, json); +const newExtrinsics = fromMetadata(registry, metadata); describe('fromMetadata', (): void => { it('should throw if an incorrect number of args is supplied', (): void => { @@ -24,7 +23,7 @@ describe('fromMetadata', (): void => { it('should return properly-encoded transactions', (): void => { expect( - createType('Extrinsic', newExtrinsics.timestamp.set([10101])).toU8a() + createType(registry, 'Extrinsic', newExtrinsics.timestamp.set([10101])).toU8a() ).toEqual( new Uint8Array([ // length (encoded) diff --git a/packages/metadata/src/Decorated/extrinsics/fromMetadata/index.ts b/packages/metadata/src/Decorated/extrinsics/fromMetadata/index.ts index 13ae839e8651..8ca9f459e496 100644 --- a/packages/metadata/src/Decorated/extrinsics/fromMetadata/index.ts +++ b/packages/metadata/src/Decorated/extrinsics/fromMetadata/index.ts @@ -2,12 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Calls, ModulesWithCalls } from '@polkadot/types/types'; +import { Calls, ModulesWithCalls, Registry, RegistryMetadata, RegistryMetadataModule } from '@polkadot/types/types'; import { stringCamelCase } from '@polkadot/util'; -import { ModuleMetadataV8 } from '../../../Metadata/v8/Metadata'; -import Metadata from '../../../Metadata'; import extrinsics from '../'; import createUnchecked from './createUnchecked'; @@ -16,16 +14,16 @@ import createUnchecked from './createUnchecked'; * * @param metadata - The metadata */ -export default function fromMetadata (metadata: Metadata): ModulesWithCalls { +export default function fromMetadata (registry: Registry, metadata: RegistryMetadata): ModulesWithCalls { return metadata.asLatest.modules .filter(({ calls }): boolean => calls.isSome) - .reduce((result, { calls, name }: ModuleMetadataV8, sectionIndex): ModulesWithCalls => { + .reduce((result, { calls, name }: RegistryMetadataModule, sectionIndex): ModulesWithCalls => { const section = stringCamelCase(name.toString()); result[section] = calls.unwrap().reduce((newModule: Calls, callMetadata, methodIndex): Calls => { const method = stringCamelCase(callMetadata.name.toString()); - newModule[method] = createUnchecked(section, sectionIndex, methodIndex, callMetadata); + newModule[method] = createUnchecked(registry, section, sectionIndex, methodIndex, callMetadata); return newModule; }, {}); diff --git a/packages/metadata/src/Decorated/extrinsics/index.spec.ts b/packages/metadata/src/Decorated/extrinsics/index.spec.ts index ae25d267f854..c2f203e336dd 100644 --- a/packages/metadata/src/Decorated/extrinsics/index.spec.ts +++ b/packages/metadata/src/Decorated/extrinsics/index.spec.ts @@ -2,29 +2,22 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { ModulesWithCalls } from '@polkadot/types/types'; - -import '@polkadot/types/injector'; - import BN from 'bn.js'; import testingPairs from '@polkadot/keyring/testingPairs'; -import { createType, Metadata } from '@polkadot/types'; +import { createType, Metadata, TypeRegistry } from '@polkadot/types'; import metadataStatic from '../../Metadata/static'; import fromMetadata from './fromMetadata'; const keyring = testingPairs({ type: 'ed25519' }, false); +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, metadataStatic); +const extrinsics = fromMetadata(registry, metadata); describe('extrinsics', (): void => { - let extrinsics: ModulesWithCalls; - - beforeEach((): void => { - extrinsics = fromMetadata(new Metadata(metadataStatic)); - }); - it('encodes an actual transfer (actual data)', (): void => { expect( - createType('Extrinsic', + createType(registry, 'Extrinsic', extrinsics.balances.transfer(keyring.bob.publicKey, 6969) ).sign(keyring.alice, { blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.spec.ts b/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.spec.ts index 539fa40fb4c4..cc6871c7764e 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.spec.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.spec.ts @@ -2,16 +2,18 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { StorageHasher, Text } from '@polkadot/types'; +import { StorageHasher, Text, TypeRegistry } from '@polkadot/types'; import { StorageEntry } from '@polkadot/types/primitive/StorageKey'; import { stringToU8a, u8aConcat, u8aToHex } from '@polkadot/util'; import createFunction from './createFunction'; describe('createFunction', (): void => { + const registry = new TypeRegistry(); + it('should create timestamp.now correctly', (): void => { expect( - createFunction({ + createFunction(registry, { prefix: 'Timestamp', section: 'timestamp', method: 'Now', @@ -25,6 +27,7 @@ describe('createFunction', (): void => { it('allows overrides on key (keeping name)', (): void => { expect( createFunction( + registry, { prefix: 'Substrate', section: 'substrate', @@ -44,6 +47,7 @@ describe('createFunction', (): void => { expect( createFunction( + registry, { prefix: 'Substrate', section: 'substrate', @@ -67,7 +71,7 @@ describe('createFunction', (): void => { let storageFn: StorageEntry; beforeAll((): void => { - storageFn = createFunction({ + storageFn = createFunction(registry, { prefix: 'GenericAsset', section: 'genericAsset', method: 'FreeBalance', @@ -76,11 +80,11 @@ describe('createFunction', (): void => { type: { isDoubleMap: true, asDoubleMap: { - hasher: new StorageHasher('Blake2_256'), - key1: new Text('AccountId'), - key2: new Text('AccountId'), - value: new Text('Balance'), - key2Hasher: new Text('twox_128') + hasher: new StorageHasher(registry, 'Blake2_256'), + key1: new Text(registry, 'AccountId'), + key2: new Text(registry, 'AccountId'), + value: new Text(registry, 'Balance'), + key2Hasher: new Text(registry, 'twox_128') } } } as any @@ -100,7 +104,7 @@ describe('createFunction', (): void => { }); it('allows creates double map function with a Null type key', (): void => { - const storageFn = createFunction({ + const storageFn = createFunction(registry, { prefix: 'System', section: 'system', method: 'EventTopics', @@ -108,11 +112,11 @@ describe('createFunction', (): void => { type: { isDoubleMap: true, asDoubleMap: { - hasher: new StorageHasher('Blake2_256'), - key1: new Text('Null'), - key2: new Text('Hash'), - value: new Text('Vec<(BlockNumber,EventIndex)>'), - key2Hasher: new Text('blake2_256') + hasher: new StorageHasher(registry, 'Blake2_256'), + key1: new Text(registry, 'Null'), + key2: new Text(registry, 'Hash'), + value: new Text(registry, 'Vec<(BlockNumber,EventIndex)>'), + key2Hasher: new Text(registry, 'blake2_256') } } } as any diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.ts b/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.ts index 79cad1917eae..9f8241408e72 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/createFunction.ts @@ -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 { Codec } from '@polkadot/types/types'; +import { Codec, Registry } from '@polkadot/types/types'; import BN from 'bn.js'; import { Compact, U8a, createType, createTypeUnsafe } from '@polkadot/types/codec'; @@ -56,7 +56,7 @@ function getHashers ({ meta: { type } }: CreateItemFn): [HasherFunction, HasherF } // create a key for a DoubleMap type -function createKeyDoubleMap ({ meta: { name, type } }: CreateItemFn, rawKey: Uint8Array, args: [CreateArgType, CreateArgType], [hasher, key2Hasher]: [HasherFunction, HasherFunction?]): Uint8Array { +function createKeyDoubleMap (registry: Registry, { meta: { name, type } }: CreateItemFn, rawKey: Uint8Array, args: [CreateArgType, CreateArgType], [hasher, key2Hasher]: [HasherFunction, HasherFunction?]): Uint8Array { // since we are passing an almost-unknown through, trust, but verify assert( Array.isArray(args) && !isUndefined(args[0]) && !isNull(args[0]) && !isUndefined(args[1]) && !isNull(args[1]), @@ -66,25 +66,25 @@ function createKeyDoubleMap ({ meta: { name, type } }: CreateItemFn, rawKey: Uin const [key1, key2] = args; const type1 = type.asDoubleMap.key1.toString(); const type2 = type.asDoubleMap.key2.toString(); - const param1Encoded = u8aConcat(rawKey, createTypeUnsafe(type1, [key1]).toU8a(true)); + const param1Encoded = u8aConcat(rawKey, createTypeUnsafe(registry, type1, [key1]).toU8a(true)); const param1Hashed = hasher(param1Encoded); // If this fails it means the getHashers function failed - and we have much bigger issues - const param2Hashed = (key2Hasher as HasherFunction)(createTypeUnsafe(type2, [key2]).toU8a(true)); + const param2Hashed = (key2Hasher as HasherFunction)(createTypeUnsafe(registry, type2, [key2]).toU8a(true)); // as per createKey, always add the length prefix (underlying it is Bytes) return Compact.addLengthPrefix(u8aConcat(param1Hashed, param2Hashed)); } // create a key for either a map or a plain value -function createKey ({ meta: { name, type } }: CreateItemFn, rawKey: Uint8Array, arg: CreateArgType, hasher: (value: Uint8Array) => Uint8Array): Uint8Array { +function createKey (registry: Registry, { meta: { name, type } }: CreateItemFn, rawKey: Uint8Array, arg: CreateArgType, hasher: (value: Uint8Array) => Uint8Array): Uint8Array { let key = rawKey; if (type.isMap) { assert(!isUndefined(arg) && !isNull(arg), `${name} is a Map and requires one argument`); const mapType = type.asMap.key.toString(); - const param = createTypeUnsafe(mapType, [arg]).toU8a(); + const param = createTypeUnsafe(registry, mapType, [arg]).toU8a(); key = u8aConcat(key, param); } @@ -111,24 +111,24 @@ function expandWithMeta ({ meta, method, prefix, section }: CreateItemFn, storag } // attch the head key hashing for linked maps -function extendLinkedMap ({ meta: { documentation, name, type } }: CreateItemFn, storageFn: StorageEntry, stringKey: string, hasher: HasherFunction): StorageEntry { - const headHash = new U8a(hasher(`head of ${stringKey}`)); +function extendLinkedMap (registry: Registry, { meta: { documentation, name, type } }: CreateItemFn, storageFn: StorageEntry, stringKey: string, hasher: HasherFunction): StorageEntry { + const headHash = new U8a(registry, hasher(`head of ${stringKey}`)); const headFn: any = (): U8a => headHash; // metadata with a fallback value using the type of the key, the normal // meta fallback only applies to actual entry values, create one for head - headFn.meta = new StorageEntryMetadata({ + headFn.meta = new StorageEntryMetadata(registry, { name, - modifier: createType('StorageEntryModifierLatest', 1), // required - type: new StorageEntryType(createType('PlainTypeLatest', type.asMap.key), 0), - fallback: createType('Bytes', createTypeUnsafe(type.asMap.key.toString()).toHex()), + modifier: createType(registry, 'StorageEntryModifierLatest', 1), // required + type: new StorageEntryType(registry, createType(registry, 'PlainTypeLatest', type.asMap.key), 0), + fallback: createType(registry, 'Bytes', createTypeUnsafe(registry, type.asMap.key.toString()).toHex()), documentation }); // here we pass the section/method through as well - these are not on // the function itself, so specify these explicitly to the constructor - storageFn.headKey = createType('StorageKey', headFn, { + storageFn.headKey = createType(registry, 'StorageKey', headFn, { method: storageFn.method, section: `head of ${storageFn.section}` }); @@ -146,7 +146,7 @@ function extendLinkedMap ({ meta: { documentation, name, type } }: CreateItemFn, * are not known at runtime (from state_getMetadata), they need to be supplied * by us manually at compile time. */ -export default function createFunction (item: CreateItemFn, options: CreateItemOptions = {}): StorageEntry { +export default function createFunction (registry: Registry, item: CreateItemFn, options: CreateItemOptions = {}): StorageEntry { const { meta: { type } } = item; const [stringKey, rawKey] = createKeys(item, options); const [hasher, key2Hasher] = getHashers(item); @@ -157,13 +157,13 @@ export default function createFunction (item: CreateItemFn, options: CreateItemO // For doublemap queries the params is passed in as an tuple, [key1, key2] const _storageFn = (arg?: CreateArgType | [CreateArgType?, CreateArgType?]): Uint8Array => type.isDoubleMap - ? createKeyDoubleMap(item, rawKey, arg as [CreateArgType, CreateArgType], [hasher, key2Hasher]) - : createKey(item, rawKey, arg as CreateArgType, options.skipHashing ? NULL_HASHER : hasher); + ? createKeyDoubleMap(registry, item, rawKey, arg as [CreateArgType, CreateArgType], [hasher, key2Hasher]) + : createKey(registry, item, rawKey, arg as CreateArgType, options.skipHashing ? NULL_HASHER : hasher); const storageFn = expandWithMeta(item, _storageFn as StorageEntry); if (type.isMap && type.asMap.linked.isTrue) { - extendLinkedMap(item, storageFn, stringKey, hasher); + extendLinkedMap(registry, item, storageFn, stringKey, hasher); } return storageFn; diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/fromMetadata.spec.ts b/packages/metadata/src/Decorated/storage/fromMetadata/fromMetadata.spec.ts index 18dc2fc9066d..7f96dfc4dd92 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/fromMetadata.spec.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/fromMetadata.spec.ts @@ -2,16 +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 '@polkadot/types/injector'; - import testingPairs from '@polkadot/keyring/testingPairs'; +import { TypeRegistry } from '@polkadot/types'; import { u8aToHex } from '@polkadot/util'; import rpcMetadata from '../../../Metadata/static'; import Decorated from '../../Decorated'; +const registry = new TypeRegistry(); const keyring = testingPairs({ type: 'ed25519' }); -const decorated = new Decorated(rpcMetadata); +const decorated = new Decorated(registry, rpcMetadata); describe('fromMetadata', (): void => { it('should throw if the storage function expects an argument', (): void => { diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/getHasher.spec.ts b/packages/metadata/src/Decorated/storage/fromMetadata/getHasher.spec.ts index be0c858b69b9..66ff26957468 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/getHasher.spec.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/getHasher.spec.ts @@ -2,16 +2,18 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { StorageHasher } from '@polkadot/types'; +import { StorageHasher, TypeRegistry } from '@polkadot/types'; import { stringToU8a } from '@polkadot/util'; import { xxhashAsU8a } from '@polkadot/util-crypto'; import getHasher from './getHasher'; describe('getHasher', (): void => { + const registry = new TypeRegistry(); + describe('Twox64Concat', (): void => { it('matches the foo test from Rust', (): void => { - const hasher = getHasher(new StorageHasher('Twox64Concat')); + const hasher = getHasher(new StorageHasher(registry, 'Twox64Concat')); const hash = hasher('foo'); const xxhash = xxhashAsU8a('foo', 128); diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/index.ts b/packages/metadata/src/Decorated/storage/fromMetadata/index.ts index bd0d27996b4e..8bf5ba8b31c7 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/index.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/index.ts @@ -2,20 +2,21 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; import { ModuleStorage, Storage } from '../../types'; import { stringCamelCase, stringLowerFirst } from '@polkadot/util'; import Metadata from '../../../Metadata'; import createFunction from './createFunction'; -import { storage } from './storage'; +import getStorage from './storage'; /** * Return an object with the modules and the functions to access their storage. * * @param metadata - The metadata */ -export default function fromMetadata (metadata: Metadata): Storage { +export default function fromMetadata (registry: Registry, metadata: Metadata): Storage { return metadata.asLatest.modules.reduce((result, moduleMetadata): Storage => { if (moduleMetadata.storage.isNone) { return result; @@ -30,7 +31,7 @@ export default function fromMetadata (metadata: Metadata): Storage { result[section] = unwrapped.items.reduce((newModule, meta): ModuleStorage => { const method = meta.name.toString(); - newModule[stringLowerFirst(method)] = createFunction({ + newModule[stringLowerFirst(method)] = createFunction(metadata.registry, { meta, method, prefix, @@ -41,5 +42,5 @@ export default function fromMetadata (metadata: Metadata): Storage { }, {} as unknown as ModuleStorage); return result; - }, { ...storage }); + }, { ...getStorage(registry) }); } diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/storage.spec.ts b/packages/metadata/src/Decorated/storage/fromMetadata/storage.spec.ts index a3640df89ee3..3b6bfca1bb6f 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/storage.spec.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/storage.spec.ts @@ -2,11 +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 '@polkadot/types/injector'; +import { TypeRegistry } from '@polkadot/types'; -import { storage } from './storage'; +import getStorage from './storage'; describe('storage', (): void => { + const registry = new TypeRegistry(); + const storage = getStorage(registry); + it('should return well known keys', (): void => { expect(typeof storage.substrate).toBe('object'); diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/storage.ts b/packages/metadata/src/Decorated/storage/fromMetadata/storage.ts index f97ae7104ee1..59807b9449a5 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/storage.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/storage.ts @@ -2,9 +2,20 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { StorageEntry } from '@polkadot/types/primitive/StorageKey'; +import { Registry } from '@polkadot/types/types'; import { Storage } from '../../types'; + import * as substrate from './substrate'; -export const storage: Storage = { - substrate // Prefill storage with well known keys, as not returned by state_getMetadata -}; +export default function getStorage (registry: Registry): Storage { + return { + substrate: Object + .entries(substrate) + .reduce((storage: Record, [key, fn]): Record => { + (storage as any)[key] = fn(registry); + + return storage; + }, {}) + }; +} diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/substrate.spec.ts b/packages/metadata/src/Decorated/storage/fromMetadata/substrate.spec.ts index dc0edd7ec304..c76bc5741d14 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/substrate.spec.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/substrate.spec.ts @@ -2,40 +2,42 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '@polkadot/types/injector'; +import { TypeRegistry } from '@polkadot/types'; import { authorityCount, authorityPrefix, changesTrieConfig, childStorageKeyPrefix, code, extrinsicIndex, heapPages } from './substrate'; describe('substrate', (): void => { + const registry = new TypeRegistry(); + // @deprecated: The ':auth:' (authorityPrefix) and ':auth:len' (authorityCount) storage keys // have been removed in https://github.com/paritytech/substrate/pull/2802 it('authorityCount should return the correct storage key', (): void => { - expect(authorityCount()).toEqual(Uint8Array.from([36, 58, 97, 117, 116, 104, 58, 108, 101, 110])); // Length-prefixed + expect(authorityCount(registry)()).toEqual(Uint8Array.from([36, 58, 97, 117, 116, 104, 58, 108, 101, 110])); // Length-prefixed }); it('authorityPrefix should return the correct storage key', (): void => { - expect(authorityPrefix()).toEqual(Uint8Array.from([24, 58, 97, 117, 116, 104, 58])); // Length-prefixed + expect(authorityPrefix(registry)()).toEqual(Uint8Array.from([24, 58, 97, 117, 116, 104, 58])); // Length-prefixed }); it('changesTrieConfig should return the correct storage key', (): void => { - expect(changesTrieConfig()).toEqual(Uint8Array.from([52, 58, 99, 104, 97, 110, 103, 101, 115, 95, 116, 114, 105, 101])); // Length-prefixed + expect(changesTrieConfig(registry)()).toEqual(Uint8Array.from([52, 58, 99, 104, 97, 110, 103, 101, 115, 95, 116, 114, 105, 101])); // Length-prefixed }); it('childStorageKeyPrefix should return the correct storage key', (): void => { - expect(childStorageKeyPrefix()).toEqual(Uint8Array.from([60, 58, 99, 104, 105, 108, 100, 95, 115, 116, 111, 114, 97, 103, 101, 58])); // Length-prefixed + expect(childStorageKeyPrefix(registry)()).toEqual(Uint8Array.from([60, 58, 99, 104, 105, 108, 100, 95, 115, 116, 111, 114, 97, 103, 101, 58])); // Length-prefixed }); it('code should return the correct storage key', (): void => { - expect(code()).toEqual(Uint8Array.from([20, 58, 99, 111, 100, 101])); // Length-prefixed + expect(code(registry)()).toEqual(Uint8Array.from([20, 58, 99, 111, 100, 101])); // Length-prefixed }); it('extrinsicIndex should return the correct storage key', (): void => { - expect(extrinsicIndex()).toEqual( + expect(extrinsicIndex(registry)()).toEqual( Uint8Array.from([64, 58, 101, 120, 116, 114, 105, 110, 115, 105, 99, 95, 105, 110, 100, 101, 120]) // Length-prefixed ); }); it('heapPages should return the correct storage key', (): void => { - expect(heapPages()).toEqual(Uint8Array.from([40, 58, 104, 101, 97, 112, 112, 97, 103, 101, 115])); // Length-prefixed + expect(heapPages(registry)()).toEqual(Uint8Array.from([40, 58, 104, 101, 97, 112, 112, 97, 103, 101, 115])); // Length-prefixed }); }); diff --git a/packages/metadata/src/Decorated/storage/fromMetadata/substrate.ts b/packages/metadata/src/Decorated/storage/fromMetadata/substrate.ts index b4dd93856eaf..cb4fba66c6bb 100644 --- a/packages/metadata/src/Decorated/storage/fromMetadata/substrate.ts +++ b/packages/metadata/src/Decorated/storage/fromMetadata/substrate.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import { StorageEntry } from '@polkadot/types/primitive/StorageKey'; import { createType } from '@polkadot/types/codec'; import { StorageEntryMetadata, StorageEntryType } from '../../../Metadata/v8/Storage'; @@ -14,24 +16,20 @@ interface SubstrateMetadata { } // Small helper function to factorize code on this page. -const createRuntimeFunction = (method: string, key: string, { documentation, type }: SubstrateMetadata): StorageEntry => - createFunction( - { +function createRuntimeFunction (method: string, key: string, { documentation, type }: SubstrateMetadata): (registry: Registry) => StorageEntry { + return (registry: Registry): StorageEntry => + createFunction(registry, { meta: { - documentation: createType('Vec', [documentation]), - modifier: createType('StorageEntryModifierLatest', 1), // required - type: new StorageEntryType(type, 0), + documentation: createType(registry, 'Vec', [documentation]), + modifier: createType(registry, 'StorageEntryModifierLatest', 1), // required + type: new StorageEntryType(registry, type, 0), toJSON: (): any => key } as unknown as StorageEntryMetadata, method, prefix: 'Substrate', section: 'substrate' - }, - { - key, - skipHashing: true - } - ); + }, { key, skipHashing: true }); +} // @deprecated: The ':auth:' (authorityPrefix) and ':auth:len' (authorityCount) storage keys // have been removed in https://github.com/paritytech/substrate/pull/2802 diff --git a/packages/metadata/src/Decorated/storage/index.ts b/packages/metadata/src/Decorated/storage/index.ts index b4209a48358b..d5e2dfa93fb2 100644 --- a/packages/metadata/src/Decorated/storage/index.ts +++ b/packages/metadata/src/Decorated/storage/index.ts @@ -2,6 +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 { storage } from './fromMetadata/storage'; +import getStorage from './fromMetadata/storage'; -export default storage; +export default getStorage; diff --git a/packages/metadata/src/Metadata/MagicNumber.spec.ts b/packages/metadata/src/Metadata/MagicNumber.spec.ts index c3a8be88594b..f976278e1c7f 100644 --- a/packages/metadata/src/Metadata/MagicNumber.spec.ts +++ b/packages/metadata/src/Metadata/MagicNumber.spec.ts @@ -2,14 +2,18 @@ // 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 '@polkadot/types'; + import MagicNumber, { MAGIC_NUMBER } from './MagicNumber'; describe('MagicNumber', (): void => { + const registry = new TypeRegistry(); + it('succeeds when the magic number matches', (): void => { - expect((): MagicNumber => new MagicNumber(MAGIC_NUMBER)).not.toThrow(); + expect((): MagicNumber => new MagicNumber(registry, MAGIC_NUMBER)).not.toThrow(); }); - it('fails when the magic number missmatches', (): void => { - expect((): MagicNumber => new MagicNumber(0x12345)).toThrow(/MagicNumber/); + it('fails when the magic number mismatches', (): void => { + expect((): MagicNumber => new MagicNumber(registry, 0x12345)).toThrow(/MagicNumber/); }); }); diff --git a/packages/metadata/src/Metadata/MagicNumber.ts b/packages/metadata/src/Metadata/MagicNumber.ts index 3b746338bf80..e8a84ffefb22 100644 --- a/packages/metadata/src/Metadata/MagicNumber.ts +++ b/packages/metadata/src/Metadata/MagicNumber.ts @@ -2,16 +2,20 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import { assert } from '@polkadot/util'; import U32 from '@polkadot/types/primitive/U32'; -export const MAGIC_NUMBER = new U32(0x6174656d); // `meta`, reversed for Little Endian encoding +export const MAGIC_NUMBER = 0x6174656d; // `meta`, reversed for Little Endian encoding export default class MagicNumber extends U32 { - constructor (value?: any) { - super(value); + constructor (registry: Registry, value?: any) { + super(registry, value); + + const magic = new U32(registry, MAGIC_NUMBER); - assert(this.eq(MAGIC_NUMBER), `MagicNumber mismatch: expected ${MAGIC_NUMBER.toHex()}, found ${this.toHex()}`); + assert(this.eq(magic), `MagicNumber mismatch: expected ${magic.toHex()}, found ${this.toHex()}`); } } diff --git a/packages/metadata/src/Metadata/Metadata.ts b/packages/metadata/src/Metadata/Metadata.ts index 5b3693f3ea65..dbde50c97101 100644 --- a/packages/metadata/src/Metadata/Metadata.ts +++ b/packages/metadata/src/Metadata/Metadata.ts @@ -2,7 +2,9 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { isHex, hexToU8a, u8aConcat } from '@polkadot/util'; +import { Registry } from '@polkadot/types/types'; + +import { bnToU8a, isHex, hexToU8a, u8aConcat } from '@polkadot/util'; import { MAGIC_NUMBER } from './MagicNumber'; import MetadataVersioned from './MetadataVersioned'; @@ -13,20 +15,20 @@ import MetadataVersioned from './MetadataVersioned'; * The versioned runtime metadata as a decoded structure */ export default class Metadata extends MetadataVersioned { - constructor (value?: Uint8Array | string) { - super(Metadata.decodeMetadata(value)); + constructor (registry: Registry, value?: Uint8Array | string) { + super(registry, Metadata.decodeMetadata(registry, value)); } // first we try and parse using the versioned structure, if this does fail, // we adjust with the magic number and a manual version and re-try. As soon as // we remove support for V0, we will just do a new here - private static decodeMetadata (_value: Uint8Array | string = new Uint8Array()): MetadataVersioned { + private static decodeMetadata (registry: Registry, _value: Uint8Array | string = new Uint8Array()): MetadataVersioned { const value = isHex(_value) ? hexToU8a(_value) : _value; try { - return new MetadataVersioned(value); + return new MetadataVersioned(registry, value); } catch (error) { if (error.message.indexOf('MagicNumber mismatch') === -1) { throw error; @@ -34,8 +36,9 @@ export default class Metadata extends MetadataVersioned { } return new MetadataVersioned( + registry, u8aConcat( - MAGIC_NUMBER.toU8a(), // manually add the magic number + bnToU8a(MAGIC_NUMBER), // manually add the magic number Uint8Array.from([0]), // add the version for the original value // the actual data as retrieved ) diff --git a/packages/metadata/src/Metadata/MetadataVersioned.ts b/packages/metadata/src/Metadata/MetadataVersioned.ts index 53b677385d6d..91bc5a578e80 100644 --- a/packages/metadata/src/Metadata/MetadataVersioned.ts +++ b/packages/metadata/src/Metadata/MetadataVersioned.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { MetadataV0, MetadataV1 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { assert } from '@polkadot/util'; @@ -32,8 +33,8 @@ type MetaVersions = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; type MetaAsX = 'asV0' | 'asV1' | 'asV2' | 'asV3' | 'asV4' | 'asV5' | 'asV6' | 'asV7'; class MetadataEnum extends Enum { - constructor (value?: any, index?: number) { - super({ + constructor (registry: Registry, value?: any, index?: number) { + super(registry, { V0: 'MetadataV0', // once rolled-out, can replace this with MetadataDeprecated V1: 'MetadataV1', // once rolled-out, can replace this with MetadataDeprecated V2: MetadataV2, // once rolled-out, can replace this with MetadataDeprecated @@ -206,11 +207,13 @@ class MetadataEnum extends Enum { export default class MetadataVersioned extends Struct { private _converted: Map = new Map(); - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { magicNumber: MagicNumber, metadata: MetadataEnum }, value); + + registry.setMetadata(this); } private assertVersion (version: number): boolean { @@ -219,7 +222,7 @@ export default class MetadataVersioned extends Struct { return this.version === version; } - private getVersion (version: MetaVersions, fromPrev: (input: F) => T): T { + private getVersion (version: MetaVersions, fromPrev: (registry: Registry, input: F) => T): T { const asCurr: MetaAsX = `asV${version}` as any; const asPrev: MetaAsX = `asV${version - 1}` as any; @@ -228,7 +231,7 @@ export default class MetadataVersioned extends Struct { } if (!this._converted.has(version)) { - this._converted.set(version, fromPrev(this[asPrev] as F)); + this._converted.set(version, fromPrev(this.registry, this[asPrev] as F)); } return this._converted.get(version) as T; @@ -238,9 +241,9 @@ export default class MetadataVersioned extends Struct { * @description Returns the wrapped metadata as a limited calls-only (latest) version */ public get asCallsOnly (): MetadataVersioned { - return new MetadataVersioned({ + return new MetadataVersioned(this.registry, { magicNumber: this.magicNumber, - metadata: new MetadataEnum(toCallsOnly(this.asLatest), this.version) + metadata: new MetadataEnum(this.registry, toCallsOnly(this.registry, this.asLatest), this.version) }); } @@ -338,6 +341,6 @@ export default class MetadataVersioned extends Struct { } public getUniqTypes (throwError: boolean): string[] { - return getUniqTypes(this.asLatest, throwError); + return getUniqTypes(this.registry, this.asLatest, throwError); } } diff --git a/packages/metadata/src/Metadata/util/getUniqTypes.ts b/packages/metadata/src/Metadata/util/getUniqTypes.ts index 0e083c98e36f..695bb74f072c 100644 --- a/packages/metadata/src/Metadata/util/getUniqTypes.ts +++ b/packages/metadata/src/Metadata/util/getUniqTypes.ts @@ -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 { Codec } from '@polkadot/types/types'; +import { Codec, Registry } from '@polkadot/types/types'; import { Option, Vec } from '@polkadot/types/codec'; import { Text, Type } from '@polkadot/types/primitive'; @@ -159,7 +159,7 @@ function getStorageNames ({ modules }: ExtractionMetadata): string[][][] { ); } -export default function getUniqTypes (meta: ExtractionMetadata, throwError: boolean): string[] { +export default function getUniqTypes (registry: Registry, meta: ExtractionMetadata, throwError: boolean): string[] { const types = flattenUniq([ getCallNames(meta), getConstantNames(meta), @@ -167,7 +167,7 @@ export default function getUniqTypes (meta: ExtractionMetadata, throwError: bool getStorageNames(meta) ]); - validateTypes(types, throwError); + validateTypes(registry, types, throwError); return types; } diff --git a/packages/metadata/src/Metadata/util/testUtil.ts b/packages/metadata/src/Metadata/util/testUtil.ts index e2bf96e17503..6a5304a24026 100644 --- a/packages/metadata/src/Metadata/util/testUtil.ts +++ b/packages/metadata/src/Metadata/util/testUtil.ts @@ -2,13 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '@polkadot/types/injector'; - -import { Codec } from '@polkadot/types/types'; +import { Codec, Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import { createTypeUnsafe } from '@polkadot/types/codec/create'; -import Call from '@polkadot/types/primitive/Generic/Call'; import getUniqTypes from './getUniqTypes'; import Metadata from '../Metadata'; @@ -17,13 +14,9 @@ import Metadata from '../Metadata'; * Given the static `rpcData` and the `staticSubstrate` JSON file, Metadata * should decode `rpcData` and output `staticSubstrate`. */ -export function decodeLatestSubstrate ( - version: number, - rpcData: string, - staticSubstrate: object -): void { +export function decodeLatestSubstrate (registry: Registry, version: number, rpcData: string, staticSubstrate: object): void { it('decodes latest substrate properly', (): void => { - const metadata = new Metadata(rpcData); + const metadata = new Metadata(registry, rpcData); console.error(JSON.stringify(metadata.toJSON())); @@ -37,15 +30,15 @@ export function decodeLatestSubstrate ( * Given a `version`, MetadataLatest and MetadataV{version} should output the same * unique types. */ -export function toLatest (version: number, rpcData: string): void { +export function toLatest (registry: Registry, version: number, rpcData: string): void { it(`converts v${version} to v8`, (): void => { - const metadata = new Metadata(rpcData)[`asV${version}` as keyof Metadata]; - const metadataV8 = new Metadata(rpcData).asLatest; + const metadata = new Metadata(registry, rpcData)[`asV${version}` as keyof Metadata]; + const metadataV8 = new Metadata(registry, rpcData).asLatest; expect( - getUniqTypes(metadata as unknown as MetadataInterface, true) + getUniqTypes(registry, metadata as unknown as MetadataInterface, true) ).toEqual( - getUniqTypes(metadataV8, true) + getUniqTypes(registry, metadataV8, true) ); }); } @@ -53,11 +46,9 @@ export function toLatest (version: number, rpcData: strin /** * Given a Metadata, no type should throw when given its fallback value. */ -export function defaultValues (rpcData: string): void { +export function defaultValues (registry: Registry, rpcData: string): void { describe('storage with default values', (): void => { - const metadata = new Metadata(rpcData); - - Call.injectMetadata(metadata); + const metadata = new Metadata(registry, rpcData); metadata.asLatest.modules .filter(({ storage }): boolean => storage.isSome) @@ -65,7 +56,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 => createTypeUnsafe(type.toString(), [fallback]) + (): Codec => createTypeUnsafe(registry, type.toString(), [fallback]) ).not.toThrow(); }); }); diff --git a/packages/metadata/src/Metadata/util/toCallsOnly.spec.ts b/packages/metadata/src/Metadata/util/toCallsOnly.spec.ts index 87dc3f9acec3..176045b16cef 100644 --- a/packages/metadata/src/Metadata/util/toCallsOnly.spec.ts +++ b/packages/metadata/src/Metadata/util/toCallsOnly.spec.ts @@ -2,14 +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 '@polkadot/types/injector'; +import { TypeRegistry } from '@polkadot/types'; import staticData from '../static'; import Metadata from '..'; describe('toCallsOnly', (): void => { + const registry = new TypeRegistry(); + it('creates a calls-only version of the metadata', (): void => { - const stripped = new Metadata(staticData).asCallsOnly; + const stripped = new Metadata(registry, staticData).asCallsOnly; console.error(JSON.stringify(stripped)); @@ -17,8 +19,8 @@ describe('toCallsOnly', (): void => { }); it('can serialize from the input', (): void => { - const s1 = new Metadata(staticData).asCallsOnly.toU8a(); - const s2 = new Metadata(s1).asCallsOnly.toU8a(); + const s1 = new Metadata(registry, staticData).asCallsOnly.toU8a(); + const s2 = new Metadata(registry, s1).asCallsOnly.toU8a(); expect(s1).toEqual(s2); }); diff --git a/packages/metadata/src/Metadata/util/toCallsOnly.ts b/packages/metadata/src/Metadata/util/toCallsOnly.ts index 9be1639ae104..f7f4bcdc4bd8 100644 --- a/packages/metadata/src/Metadata/util/toCallsOnly.ts +++ b/packages/metadata/src/Metadata/util/toCallsOnly.ts @@ -2,13 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { AnyJsonObject, Registry } from '@polkadot/types/types'; + import MetadataV8 from '../v8'; /** * @description Convert from MetadataV8 to a stripped representation of MetadataV8 */ -export default function toCallsOnly ({ modules }: MetadataV8): any { - return new MetadataV8({ +export default function toCallsOnly (registry: Registry, { modules }: MetadataV8): AnyJsonObject | string { + return new MetadataV8(registry, { // FIXME, this needs typing, not any modules: modules.map(({ calls, name }): any => ({ name, diff --git a/packages/metadata/src/Metadata/util/validateTypes.ts b/packages/metadata/src/Metadata/util/validateTypes.ts index 20abe892a137..ce687d052674 100644 --- a/packages/metadata/src/Metadata/util/validateTypes.ts +++ b/packages/metadata/src/Metadata/util/validateTypes.ts @@ -2,9 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; import { TypeDef, TypeDefInfo, TypeDefExtVecFixed } from '@polkadot/types/codec/types'; -import { getTypeDef, getTypeRegistry } from '@polkadot/types/codec/create'; +import { getTypeDef } from '@polkadot/types/codec/create'; import flattenUniq from './flattenUniq'; @@ -36,10 +37,9 @@ function extractTypes (types: string[]): any[] { }); } -export default function validateTypes (types: string[], throwError: boolean): void { - const typeRegistry = getTypeRegistry(); +export default function validateTypes (registry: Registry, types: string[], throwError: boolean): void { const missing = flattenUniq(extractTypes(types)).filter((type): boolean => - !typeRegistry.hasType(type) + !registry.hasType(type) ); if (missing.length !== 0) { diff --git a/packages/metadata/src/Metadata/v0/Metadata.spec.ts b/packages/metadata/src/Metadata/v0/Metadata.spec.ts index 0124db7427ed..1adeb0227749 100644 --- a/packages/metadata/src/Metadata/v0/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v0/Metadata.spec.ts @@ -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 { createType } from '@polkadot/types/codec'; +import { createType, TypeRegistry } from '@polkadot/types/codec'; import Metadata from '../'; import latestSubstrateV0 from './static-substrate.json'; @@ -10,15 +10,17 @@ import rpcData from './static'; import { defaultValues, toLatest } from '../util/testUtil'; describe('Metadata', (): void => { + const registry = new TypeRegistry(); + it('works with fallback', (): void => { - const metadata = new Metadata(rpcData); - const metadataV0 = createType('MetadataV0', rpcData); + const metadata = new Metadata(registry, rpcData); + const metadataV0 = createType(registry, 'MetadataV0', rpcData); expect(metadata.asV0.toString()).toEqual(metadataV0.toString()); }); it('decodes latest substrate properly', (): void => { - const metadata = new Metadata(rpcData); + const metadata = new Metadata(registry, rpcData); const json = metadata.asV0.toJSON(); console.error(JSON.stringify(json)); @@ -28,7 +30,7 @@ describe('Metadata', (): void => { expect(json).toEqual(latestSubstrateV0); }); - toLatest(0, rpcData); + toLatest(registry, 0, rpcData); - defaultValues(rpcData); + defaultValues(registry, rpcData); }); diff --git a/packages/metadata/src/Metadata/v0/toV1.ts b/packages/metadata/src/Metadata/v0/toV1.ts index fb505e7b7d11..2167c954e8f9 100644 --- a/packages/metadata/src/Metadata/v0/toV1.ts +++ b/packages/metadata/src/Metadata/v0/toV1.ts @@ -4,49 +4,50 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV1, FunctionMetadataV1, MetadataV0, MetadataV1, ModuleMetadataV1, RuntimeModuleMetadataV0, StorageFunctionMetadataV1 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { stringUpperFirst } from '@polkadot/util'; import { createType, Option, Vec } from '@polkadot/types/codec'; import { Text } from '@polkadot/types/primitive'; -function toV1Calls ({ module: { call: { functions } } }: RuntimeModuleMetadataV0): Option { +function toV1Calls (registry: Registry, { module: { call: { functions } } }: RuntimeModuleMetadataV0): Option { return functions.length - ? new Option(Vec.with('FunctionMetadataV1'), functions) - : new Option(Vec.with('FunctionMetadataV1')); + ? new Option(registry, Vec.with('FunctionMetadataV1'), functions) + : new Option(registry, Vec.with('FunctionMetadataV1')); } -function toV1Events (metadataV0: MetadataV0, prefix: Text): Option { +function toV1Events (registry: Registry, metadataV0: MetadataV0, prefix: Text): Option { const events = metadataV0.outerEvent.events.find((event): boolean => event[0].eq(prefix)); return events - ? new Option(Vec.with('EventMetadataV1'), events[1]) - : new Option(Vec.with('EventMetadataV1')); + ? new Option(registry, Vec.with('EventMetadataV1'), events[1]) + : new Option(registry, Vec.with('EventMetadataV1')); } -function toV1Storage ({ storage }: RuntimeModuleMetadataV0): Option { +function toV1Storage (registry: Registry, { storage }: RuntimeModuleMetadataV0): Option { return storage.isSome - ? new Option('Vec', storage.unwrap().functions) - : new Option('Vec'); + ? new Option(registry, 'Vec', storage.unwrap().functions) + : new Option(registry, 'Vec'); } /** * Function that converts MetadataV0 to MetadataV1 */ -export default function toV1 (metadataV0: MetadataV0): MetadataV1 { - return createType('MetadataV1', { +export default function toV1 (registry: Registry, metadataV0: MetadataV0): MetadataV1 { + return createType(registry, 'MetadataV1', { modules: metadataV0.modules.map((mod): ModuleMetadataV1 => { // The prefix of this module (capitalized) const prefix = mod.storage.isSome ? mod.storage.unwrap().prefix.toString() : stringUpperFirst(mod.prefix.toString()); // If this module doesn't have storage, we just assume the prefix is the name capitalized - return createType('ModuleMetadataV1', { + return createType(registry, 'ModuleMetadataV1', { name: mod.prefix, // Not capitalized prefix, // Capitalized - storage: toV1Storage(mod), - calls: toV1Calls(mod), - events: toV1Events(metadataV0, mod.prefix) + storage: toV1Storage(registry, mod), + calls: toV1Calls(registry, mod), + events: toV1Events(registry, metadataV0, mod.prefix) }); }) }); diff --git a/packages/metadata/src/Metadata/v1/Metadata.spec.ts b/packages/metadata/src/Metadata/v1/Metadata.spec.ts index 2d4b0daa516c..1b63b8f1976f 100644 --- a/packages/metadata/src/Metadata/v1/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v1/Metadata.spec.ts @@ -2,14 +2,18 @@ // 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 '@polkadot/types'; + import staticSubstrate from './static-substrate.json'; import rpcData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV1', (): void => { - decodeLatestSubstrate(1, rpcData, staticSubstrate); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 1, rpcData, staticSubstrate); - toLatest(1, rpcData); + toLatest(registry, 1, rpcData); - defaultValues(rpcData); + defaultValues(registry, rpcData); }); diff --git a/packages/metadata/src/Metadata/v1/toV2.ts b/packages/metadata/src/Metadata/v1/toV2.ts index ef75f5198daa..0b2a6b9551ec 100644 --- a/packages/metadata/src/Metadata/v1/toV2.ts +++ b/packages/metadata/src/Metadata/v1/toV2.ts @@ -3,9 +3,10 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { MetadataV1 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import MetadataV2 from '../v2'; -export default function toV2 (metadataV1: MetadataV1): MetadataV2 { - return new MetadataV2(metadataV1); +export default function toV2 (registry: Registry, metadataV1: MetadataV1): MetadataV2 { + return new MetadataV2(registry, metadataV1); } diff --git a/packages/metadata/src/Metadata/v2/Metadata.spec.ts b/packages/metadata/src/Metadata/v2/Metadata.spec.ts index d947bdcd1c87..91bd2bad40a8 100644 --- a/packages/metadata/src/Metadata/v2/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v2/Metadata.spec.ts @@ -2,14 +2,18 @@ // 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 '@polkadot/types'; + import staticSubstrate from './static-substrate.json'; import rpcData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV2', (): void => { - decodeLatestSubstrate(2, rpcData, staticSubstrate); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 2, rpcData, staticSubstrate); - toLatest(2, rpcData); + toLatest(registry, 2, rpcData); - defaultValues(rpcData); + defaultValues(registry, rpcData); }); diff --git a/packages/metadata/src/Metadata/v2/Metadata.ts b/packages/metadata/src/Metadata/v2/Metadata.ts index a1096e37a0f7..ab8a6a28e509 100644 --- a/packages/metadata/src/Metadata/v2/Metadata.ts +++ b/packages/metadata/src/Metadata/v2/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV2, FunctionMetadataV2 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -18,8 +19,8 @@ import { StorageFunctionMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV2 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', prefix: 'Text', storage: Option.with(Vec.with(StorageFunctionMetadata)), @@ -70,8 +71,8 @@ export class ModuleMetadataV2 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV2 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV2) }, value); } diff --git a/packages/metadata/src/Metadata/v2/Storage.ts b/packages/metadata/src/Metadata/v2/Storage.ts index 6ee8a76c2003..c8883082e237 100644 --- a/packages/metadata/src/Metadata/v2/Storage.ts +++ b/packages/metadata/src/Metadata/v2/Storage.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { MapTypeV2, PlainTypeV2, StorageFunctionModifierV2 } from '@polkadot/types/interfaces/metadata'; -import { AnyNumber } from '@polkadot/types/types'; +import { AnyNumber, Registry } from '@polkadot/types/types'; import { assert } from '@polkadot/util'; @@ -14,8 +14,8 @@ import Bytes from '@polkadot/types/primitive/Bytes'; import Text from '@polkadot/types/primitive/Text'; export class StorageFunctionType extends Enum { - constructor (value?: any, index?: number) { - super({ + constructor (registry: Registry, value?: any, index?: number) { + super(registry, { Type: 'PlainTypeV2', Map: 'MapTypeV2' }, value, index); @@ -83,8 +83,8 @@ export interface StorageFunctionMetadataValue { * The definition of a storage function */ export class StorageFunctionMetadata extends Struct { - constructor (value?: StorageFunctionMetadataValue | Uint8Array) { - super({ + constructor (registry: Registry, value?: StorageFunctionMetadataValue | Uint8Array) { + super(registry, { name: 'Text', modifier: 'StorageFunctionModifierV2', type: StorageFunctionType, diff --git a/packages/metadata/src/Metadata/v2/toV3.ts b/packages/metadata/src/Metadata/v2/toV3.ts index 03ae40e94357..d00b7f07017b 100644 --- a/packages/metadata/src/Metadata/v2/toV3.ts +++ b/packages/metadata/src/Metadata/v2/toV3.ts @@ -2,9 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import MetadataV2 from './Metadata'; import MetadataV3 from '../v3'; -export default function toV3 (metadataV2: MetadataV2): MetadataV3 { - return new MetadataV3(metadataV2); +export default function toV3 (registry: Registry, metadataV2: MetadataV2): MetadataV3 { + return new MetadataV3(registry, metadataV2); } diff --git a/packages/metadata/src/Metadata/v3/Metadata.spec.ts b/packages/metadata/src/Metadata/v3/Metadata.spec.ts index d157936bb1df..84ebe8823787 100644 --- a/packages/metadata/src/Metadata/v3/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v3/Metadata.spec.ts @@ -2,14 +2,18 @@ // 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 '@polkadot/types'; + import staticSubstrate from './static-substrate.json'; import rpcData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV3', (): void => { - decodeLatestSubstrate(3, rpcData, staticSubstrate); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 3, rpcData, staticSubstrate); - toLatest(3, rpcData); + toLatest(registry, 3, rpcData); - defaultValues(rpcData); + defaultValues(registry, rpcData); }); diff --git a/packages/metadata/src/Metadata/v3/Metadata.ts b/packages/metadata/src/Metadata/v3/Metadata.ts index e7971efe090d..ef5c3e7412b8 100644 --- a/packages/metadata/src/Metadata/v3/Metadata.ts +++ b/packages/metadata/src/Metadata/v3/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV3, FunctionMetadataV3 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -18,8 +19,8 @@ import { StorageFunctionMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV3 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', prefix: 'Text', storage: Option.with(Vec.with(StorageFunctionMetadata)), @@ -70,8 +71,8 @@ export class ModuleMetadataV3 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV3 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV3) }, value); } diff --git a/packages/metadata/src/Metadata/v3/Storage.ts b/packages/metadata/src/Metadata/v3/Storage.ts index 4917734d6410..d1fec3adcb07 100644 --- a/packages/metadata/src/Metadata/v3/Storage.ts +++ b/packages/metadata/src/Metadata/v3/Storage.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { DoubleMapTypeV3, MapTypeV3, PlainTypeV3, StorageFunctionModifierV3 } from '@polkadot/types/interfaces/metadata'; -import { AnyNumber } from '@polkadot/types/types'; +import { AnyNumber, Registry } from '@polkadot/types/types'; import { assert } from '@polkadot/util'; @@ -14,8 +14,8 @@ import Bytes from '@polkadot/types/primitive/Bytes'; import Text from '@polkadot/types/primitive/Text'; export class StorageFunctionType extends Enum { - constructor (value?: any, index?: number) { - super({ + constructor (registry: Registry, value?: any, index?: number) { + super(registry, { Type: 'PlainTypeV3', Map: 'MapTypeV3', DoubleMap: 'DoubleMapTypeV3' @@ -104,8 +104,8 @@ export interface StorageFunctionMetadataValue { * The definition of a storage function */ export class StorageFunctionMetadata extends Struct { - constructor (value?: StorageFunctionMetadataValue | Uint8Array) { - super({ + constructor (registry: Registry, value?: StorageFunctionMetadataValue | Uint8Array) { + super(registry, { name: 'Text', modifier: 'StorageFunctionModifierV3', type: StorageFunctionType, diff --git a/packages/metadata/src/Metadata/v3/toV4.ts b/packages/metadata/src/Metadata/v3/toV4.ts index ba2b68718bd1..2648e695b5b3 100644 --- a/packages/metadata/src/Metadata/v3/toV4.ts +++ b/packages/metadata/src/Metadata/v3/toV4.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import StorageHasher from '@polkadot/types/primitive/StorageHasher'; import { createType, Option, Vec } from '@polkadot/types/codec'; @@ -14,7 +16,7 @@ import { StorageFunctionMetadata as StorageFunctionMetadataV3 } from './Storage' /** * Convert V3 StorageFunction to V4 StorageFunction */ -function toV4StorageFunction (storageFn: StorageFunctionMetadataV3): StorageFunctionMetadata { +function toV4StorageFunction (registry: Registry, storageFn: StorageFunctionMetadataV3): StorageFunctionMetadata { const { documentation, fallback, modifier, name, type } = storageFn; // Convert the old type to the new type: there is one new field @@ -23,26 +25,26 @@ function toV4StorageFunction (storageFn: StorageFunctionMetadataV3): StorageFunc const [newType, index] = type.isPlainType ? [type, 0] : type.isMap - ? [createType('MapTypeV4', { - hasher: new StorageHasher('Twox128'), + ? [createType(registry, 'MapTypeV4', { + hasher: new StorageHasher(registry, 'Twox128'), key: type.asMap.key, value: type.asMap.value, linked: type.asMap.linked }), 1] - : [createType('DoubleMapTypeV4', { - hasher: new StorageHasher('Twox128'), + : [createType(registry, 'DoubleMapTypeV4', { + hasher: new StorageHasher(registry, 'Twox128'), key1: type.asDoubleMap.key1, key2: type.asDoubleMap.key2, value: type.asDoubleMap.value, key2Hasher: type.asDoubleMap.key2Hasher }), 2]; - return new StorageFunctionMetadata({ + return new StorageFunctionMetadata(registry, { documentation, fallback, name, modifier, - type: new StorageFunctionType(newType, index) + type: new StorageFunctionType(registry, newType, index) }); } @@ -50,16 +52,20 @@ function toV4StorageFunction (storageFn: StorageFunctionMetadataV3): StorageFunc * Convert from MetadataV3 to MetadataV4 * See https://github.com/paritytech/substrate/pull/2268 for details */ -export default function toV4 ({ modules }: MetadataV3): MetadataV4 { - return new MetadataV4({ +export default function toV4 (registry: Registry, { modules }: MetadataV3): MetadataV4 { + return new MetadataV4(registry, { modules: modules.map(({ calls, events, name, prefix, storage }): ModuleMetadataV4 => - new ModuleMetadataV4({ + new ModuleMetadataV4(registry, { calls, events, name, prefix, storage: storage.isSome - ? new Option(Vec.with(StorageFunctionMetadata), storage.unwrap().map(toV4StorageFunction)) + ? new Option( + registry, + Vec.with(StorageFunctionMetadata), + storage.unwrap().map((v): StorageFunctionMetadata => toV4StorageFunction(registry, v)) + ) : undefined }) ) diff --git a/packages/metadata/src/Metadata/v4/Metadata.spec.ts b/packages/metadata/src/Metadata/v4/Metadata.spec.ts index 5b3ee6bd0b88..5f77cc5d8982 100644 --- a/packages/metadata/src/Metadata/v4/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v4/Metadata.spec.ts @@ -2,14 +2,18 @@ // 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 '@polkadot/types'; + import staticSubstrate from './static-substrate.json'; import rpcData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV4', (): void => { - decodeLatestSubstrate(4, rpcData, staticSubstrate); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 4, rpcData, staticSubstrate); - toLatest(4, rpcData); + toLatest(registry, 4, rpcData); - defaultValues(rpcData); + defaultValues(registry, rpcData); }); diff --git a/packages/metadata/src/Metadata/v4/Metadata.ts b/packages/metadata/src/Metadata/v4/Metadata.ts index 770ac8ca2b20..0e41dc2d0c7f 100644 --- a/packages/metadata/src/Metadata/v4/Metadata.ts +++ b/packages/metadata/src/Metadata/v4/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV4, FunctionMetadataV4 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -17,8 +18,8 @@ import { StorageFunctionMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV4 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', prefix: 'Text', storage: Option.with(Vec.with(StorageFunctionMetadata)), @@ -69,8 +70,8 @@ export class ModuleMetadataV4 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV4 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV4) }, value); } diff --git a/packages/metadata/src/Metadata/v4/Storage.ts b/packages/metadata/src/Metadata/v4/Storage.ts index 5ecc0262c103..fd162aee06db 100644 --- a/packages/metadata/src/Metadata/v4/Storage.ts +++ b/packages/metadata/src/Metadata/v4/Storage.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { DoubleMapTypeV4, MapTypeV4, PlainTypeV4, StorageFunctionModifierV4 } from '@polkadot/types/interfaces/metadata'; -import { AnyNumber } from '@polkadot/types/types'; +import { AnyNumber, Registry } from '@polkadot/types/types'; import { assert } from '@polkadot/util'; @@ -14,8 +14,8 @@ import Bytes from '@polkadot/types/primitive/Bytes'; import Text from '@polkadot/types/primitive/Text'; export class StorageFunctionType extends Enum { - constructor (value?: any, index?: number) { - super({ + constructor (registry: Registry, value?: any, index?: number) { + super(registry, { Type: 'PlainTypeV4', Map: 'MapTypeV4', DoubleMap: 'DoubleMapTypeV4' @@ -104,8 +104,8 @@ export interface StorageFunctionMetadataValue { * The definition of a storage function */ export class StorageFunctionMetadata extends Struct { - constructor (value?: StorageFunctionMetadataValue | Uint8Array) { - super({ + constructor (registry: Registry, value?: StorageFunctionMetadataValue | Uint8Array) { + super(registry, { name: 'Text', modifier: 'StorageFunctionModifierV4', type: StorageFunctionType, diff --git a/packages/metadata/src/Metadata/v4/toV5.ts b/packages/metadata/src/Metadata/v4/toV5.ts index d6e6402295e1..6b30d930c004 100644 --- a/packages/metadata/src/Metadata/v4/toV5.ts +++ b/packages/metadata/src/Metadata/v4/toV5.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import { assert } from '@polkadot/util'; import { createType, Option, Vec } from '@polkadot/types/codec'; @@ -21,37 +23,37 @@ const hasherMap: Map = new Map([ ['twox_64_concat', 'Twox64Concat'] ]); -function toStorageHasher (text: Text): StorageHasher { +function toStorageHasher (registry: Registry, text: Text): StorageHasher { const mapped = hasherMap.get(text.toString()); assert(mapped, `Invalid Storage hasher: ${text.toString()}`); - return new StorageHasher(mapped); + return new StorageHasher(registry, mapped); } /** * Convert V4 StorageFunction to V5 StorageFunction */ -function toV5StorageFunction (storageFn: StorageFunctionMetadataV4): StorageFunctionMetadata { +function toV5StorageFunction (registry: Registry, storageFn: StorageFunctionMetadataV4): StorageFunctionMetadata { const { documentation, fallback, modifier, name, type } = storageFn; const [newType, index] = type.isPlainType ? [type, 0] : type.isMap ? [type.asMap, 1] - : [createType('DoubleMapTypeV5', { + : [createType(registry, 'DoubleMapTypeV5', { hasher: type.asDoubleMap.hasher, key1: type.asDoubleMap.key1, key2: type.asDoubleMap.key2, value: type.asDoubleMap.value, - key2Hasher: toStorageHasher(type.asDoubleMap.key2Hasher) + key2Hasher: toStorageHasher(registry, type.asDoubleMap.key2Hasher) }), 2]; - return new StorageFunctionMetadata({ + return new StorageFunctionMetadata(registry, { documentation, fallback, name, modifier, - type: new StorageFunctionType(newType, index) + type: new StorageFunctionType(registry, newType, index) }); } @@ -59,14 +61,18 @@ function toV5StorageFunction (storageFn: StorageFunctionMetadataV4): StorageFunc * Convert from MetadataV4 to MetadataV5 * See https://github.com/paritytech/substrate/pull/2836/files for details */ -export default function toV5 ({ modules }: MetadataV4): MetadataV5 { - return new MetadataV5({ +export default function toV5 (registry: Registry, { modules }: MetadataV4): MetadataV5 { + return new MetadataV5(registry, { modules: modules.map(({ calls, events, name, prefix, storage }): ModuleMetadataV5 => - new ModuleMetadataV5({ + new ModuleMetadataV5(registry, { name, prefix, storage: storage.isSome - ? new Option(Vec.with(StorageFunctionMetadata), storage.unwrap().map(toV5StorageFunction)) + ? new Option( + registry, + Vec.with(StorageFunctionMetadata), + storage.unwrap().map((v): StorageFunctionMetadata => toV5StorageFunction(registry, v)) + ) : undefined, calls, events diff --git a/packages/metadata/src/Metadata/v5/Metadata.spec.ts b/packages/metadata/src/Metadata/v5/Metadata.spec.ts index c243375ec695..5041bef54bd4 100644 --- a/packages/metadata/src/Metadata/v5/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v5/Metadata.spec.ts @@ -2,6 +2,8 @@ // 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 '@polkadot/types'; + import polkadotJson from './static-polkadot.json'; import substrateJson from './static-substrate.json'; import polkadotData from './static.polkadot'; @@ -9,17 +11,21 @@ import substrateData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV5 (substrate)', (): void => { - decodeLatestSubstrate(5, substrateData, substrateJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 5, substrateData, substrateJson); - toLatest(5, substrateData); + toLatest(registry, 5, substrateData); - defaultValues(substrateData); + defaultValues(registry, substrateData); }); describe('MetadataV5 (polkadot)', (): void => { - decodeLatestSubstrate(5, polkadotData, polkadotJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 5, polkadotData, polkadotJson); - toLatest(5, polkadotData); + toLatest(registry, 5, polkadotData); - defaultValues(polkadotData); + defaultValues(registry, polkadotData); }); diff --git a/packages/metadata/src/Metadata/v5/Metadata.ts b/packages/metadata/src/Metadata/v5/Metadata.ts index 7641b8f7dc91..9313ed6100ec 100644 --- a/packages/metadata/src/Metadata/v5/Metadata.ts +++ b/packages/metadata/src/Metadata/v5/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV5, FunctionMetadataV5 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -17,8 +18,8 @@ import { StorageFunctionMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV5 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', prefix: 'Text', storage: Option.with(Vec.with(StorageFunctionMetadata)), @@ -69,8 +70,8 @@ export class ModuleMetadataV5 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV5 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV5) }, value); } diff --git a/packages/metadata/src/Metadata/v5/Storage.ts b/packages/metadata/src/Metadata/v5/Storage.ts index b613c571b5ea..4ca0908aa005 100644 --- a/packages/metadata/src/Metadata/v5/Storage.ts +++ b/packages/metadata/src/Metadata/v5/Storage.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { DoubleMapTypeV5, MapTypeV5, PlainTypeV5, StorageFunctionModifierV5 } from '@polkadot/types/interfaces/metadata'; -import { AnyNumber } from '@polkadot/types/types'; +import { AnyNumber, Registry } from '@polkadot/types/types'; import { assert } from '@polkadot/util'; @@ -14,8 +14,8 @@ import Bytes from '@polkadot/types/primitive/Bytes'; import Text from '@polkadot/types/primitive/Text'; export class StorageFunctionType extends Enum { - constructor (value?: any, index?: number) { - super({ + constructor (registry: Registry, value?: any, index?: number) { + super(registry, { Type: 'PlainTypeV5', Map: 'MapTypeV5', DoubleMap: 'DoubleMapTypeV5' @@ -104,8 +104,8 @@ export interface StorageFunctionMetadataValue { * The definition of a storage function */ export class StorageFunctionMetadata extends Struct { - constructor (value?: StorageFunctionMetadataValue | Uint8Array) { - super({ + constructor (registry: Registry, value?: StorageFunctionMetadataValue | Uint8Array) { + super(registry, { name: 'Text', modifier: 'StorageFunctionModifierV5', type: StorageFunctionType, diff --git a/packages/metadata/src/Metadata/v5/toV6.ts b/packages/metadata/src/Metadata/v5/toV6.ts index 4b906d045f11..c5652b9d9962 100644 --- a/packages/metadata/src/Metadata/v5/toV6.ts +++ b/packages/metadata/src/Metadata/v5/toV6.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import MetadataV5 from './Metadata'; import MetadataV6 from '../v6'; import { ModuleMetadataV6 } from '../v6/Metadata'; @@ -10,10 +12,10 @@ import { ModuleMetadataV6 } from '../v6/Metadata'; * Convert from MetadataV5 to MetadataV6 * See https://github.com/polkadot-js/api/issues/1043 for details */ -export default function toV6 ({ modules }: MetadataV5): MetadataV6 { - return new MetadataV6({ +export default function toV6 (registry: Registry, { modules }: MetadataV5): MetadataV6 { + return new MetadataV6(registry, { modules: modules.map(({ calls, events, name, prefix, storage }): ModuleMetadataV6 => - new ModuleMetadataV6({ + new ModuleMetadataV6(registry, { calls, constants: [], events, diff --git a/packages/metadata/src/Metadata/v6/Metadata.spec.ts b/packages/metadata/src/Metadata/v6/Metadata.spec.ts index 50ca1ae046d4..ae179b967032 100644 --- a/packages/metadata/src/Metadata/v6/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v6/Metadata.spec.ts @@ -2,6 +2,8 @@ // 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 '@polkadot/types'; + import polkadotJson from './static-polkadot.json'; import substrateJson from './static-substrate.json'; import polkadotData from './static.polkadot'; @@ -9,17 +11,21 @@ import substrateData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV6 (substrate)', (): void => { - decodeLatestSubstrate(6, substrateData, substrateJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 6, substrateData, substrateJson); - toLatest(6, substrateData); + toLatest(registry, 6, substrateData); - defaultValues(substrateData); + defaultValues(registry, substrateData); }); describe('MetadataV6 (polkadot)', (): void => { - decodeLatestSubstrate(6, polkadotData, polkadotJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 6, polkadotData, polkadotJson); - toLatest(6, polkadotData); + toLatest(registry, 6, polkadotData); - defaultValues(polkadotData); + defaultValues(registry, polkadotData); }); diff --git a/packages/metadata/src/Metadata/v6/Metadata.ts b/packages/metadata/src/Metadata/v6/Metadata.ts index b6a6d1667aa6..e1effa009a85 100644 --- a/packages/metadata/src/Metadata/v6/Metadata.ts +++ b/packages/metadata/src/Metadata/v6/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV6, FunctionMetadataV6, ModuleConstantMetadataV6 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -17,8 +18,8 @@ import { StorageEntryMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV6 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', prefix: 'Text', storage: Option.with(Vec.with(StorageEntryMetadata)), @@ -77,8 +78,8 @@ export class ModuleMetadataV6 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV6 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV6) }, value); } diff --git a/packages/metadata/src/Metadata/v6/toV7.ts b/packages/metadata/src/Metadata/v6/toV7.ts index 652e5fb5568d..4a1bc4673ee0 100644 --- a/packages/metadata/src/Metadata/v6/toV7.ts +++ b/packages/metadata/src/Metadata/v6/toV7.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import MetadataV6 from './Metadata'; import MetadataV7 from '../v7'; import { ModuleMetadataV7 } from '../v7/Metadata'; @@ -10,16 +12,16 @@ import { StorageMetadata as StorageV7 } from '../v7/Storage'; /** * Convert from MetadataV6 to MetadataV7 */ -export default function toV7 ({ modules }: MetadataV6): MetadataV7 { - return new MetadataV7({ +export default function toV7 (registry: Registry, { modules }: MetadataV6): MetadataV7 { + return new MetadataV7(registry, { modules: modules.map(({ calls, constants, events, name, prefix, storage }): ModuleMetadataV7 => - new ModuleMetadataV7({ + new ModuleMetadataV7(registry, { calls, constants, events, name, storage: storage.isSome - ? new StorageV7({ prefix, items: storage.unwrap() }) + ? new StorageV7(registry, { prefix, items: storage.unwrap() }) : null }) ) diff --git a/packages/metadata/src/Metadata/v7/Metadata.spec.ts b/packages/metadata/src/Metadata/v7/Metadata.spec.ts index aa4ddd65d482..372f6fd8defa 100644 --- a/packages/metadata/src/Metadata/v7/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v7/Metadata.spec.ts @@ -2,6 +2,8 @@ // 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 '@polkadot/types'; + import polkadotJson from './static-polkadot.json'; import substrateJson from './static-substrate.json'; import polkadotData from './static.polkadot'; @@ -9,17 +11,21 @@ import substrateData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV7 (substrate)', (): void => { - decodeLatestSubstrate(7, substrateData, substrateJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 7, substrateData, substrateJson); - toLatest(7, substrateData); + toLatest(registry, 7, substrateData); - defaultValues(substrateData); + defaultValues(registry, substrateData); }); describe('MetadataV7 (polkadot)', (): void => { - decodeLatestSubstrate(7, polkadotData, polkadotJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 7, polkadotData, polkadotJson); - toLatest(7, polkadotData); + toLatest(registry, 7, polkadotData); - defaultValues(polkadotData); + defaultValues(registry, polkadotData); }); diff --git a/packages/metadata/src/Metadata/v7/Metadata.ts b/packages/metadata/src/Metadata/v7/Metadata.ts index 7d02115b2716..2743057600a0 100644 --- a/packages/metadata/src/Metadata/v7/Metadata.ts +++ b/packages/metadata/src/Metadata/v7/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { EventMetadataV7, FunctionMetadataV7, ModuleConstantMetadataV7 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -17,8 +18,8 @@ import { StorageMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV7 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', storage: Option.with(StorageMetadata), calls: Option.with('Vec'), @@ -69,8 +70,8 @@ export class ModuleMetadataV7 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV7 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV7) }, value); } diff --git a/packages/metadata/src/Metadata/v7/Storage.ts b/packages/metadata/src/Metadata/v7/Storage.ts index 9af1ee73965e..5cf3c32628e2 100644 --- a/packages/metadata/src/Metadata/v7/Storage.ts +++ b/packages/metadata/src/Metadata/v7/Storage.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import Struct from '@polkadot/types/codec/Struct'; import Vec from '@polkadot/types/codec/Vec'; import Text from '@polkadot/types/primitive/Text'; @@ -15,8 +17,8 @@ export { }; export class StorageMetadata extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { prefix: 'Text', // NOTE renamed to items from entries (since Struct already has entries from Map) items: Vec.with(StorageEntryMetadata) diff --git a/packages/metadata/src/Metadata/v7/toV8.ts b/packages/metadata/src/Metadata/v7/toV8.ts index a73da173581a..e0adb1252b22 100644 --- a/packages/metadata/src/Metadata/v7/toV8.ts +++ b/packages/metadata/src/Metadata/v7/toV8.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import MetadataV7 from './Metadata'; import MetadataV8 from '../v8'; import { ModuleMetadataV8 } from '../v8/Metadata'; @@ -9,10 +11,10 @@ import { ModuleMetadataV8 } from '../v8/Metadata'; /** * Convert from MetadataV7 to MetadataV8 */ -export default function toV8 ({ modules }: MetadataV7): MetadataV8 { - return new MetadataV8({ +export default function toV8 (registry: Registry, { modules }: MetadataV7): MetadataV8 { + return new MetadataV8(registry, { modules: modules.map(({ calls, constants, events, name, storage }): ModuleMetadataV8 => - new ModuleMetadataV8({ + new ModuleMetadataV8(registry, { calls, constants, errors: [], diff --git a/packages/metadata/src/Metadata/v8/Metadata.spec.ts b/packages/metadata/src/Metadata/v8/Metadata.spec.ts index 68f410895364..87f22268ad61 100644 --- a/packages/metadata/src/Metadata/v8/Metadata.spec.ts +++ b/packages/metadata/src/Metadata/v8/Metadata.spec.ts @@ -2,14 +2,18 @@ // 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 '@polkadot/types'; + import substrateJson from './static-substrate.json'; import substrateData from './static'; import { decodeLatestSubstrate, defaultValues, toLatest } from '../util/testUtil'; describe('MetadataV8 (substrate)', (): void => { - decodeLatestSubstrate(8, substrateData, substrateJson); + const registry = new TypeRegistry(); + + decodeLatestSubstrate(registry, 8, substrateData, substrateJson); - toLatest(8, substrateData); + toLatest(registry, 8, substrateData); - defaultValues(substrateData); + defaultValues(registry, substrateData); }); diff --git a/packages/metadata/src/Metadata/v8/Metadata.ts b/packages/metadata/src/Metadata/v8/Metadata.ts index 8059213a3379..c5158f1cfb1c 100644 --- a/packages/metadata/src/Metadata/v8/Metadata.ts +++ b/packages/metadata/src/Metadata/v8/Metadata.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ErrorMetadataV8, EventMetadataV8, FunctionMetadataV8, ModuleConstantMetadataV8 } from '@polkadot/types/interfaces/metadata'; +import { Registry } from '@polkadot/types/types'; import { MetadataInterface } from '../types'; import Option from '@polkadot/types/codec/Option'; @@ -33,8 +34,8 @@ import { StorageMetadata } from './Storage'; * The definition of a module in the system */ export class ModuleMetadataV8 extends Struct { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { name: 'Text', storage: Option.with(StorageMetadata), calls: Option.with('Vec'), @@ -93,8 +94,8 @@ export class ModuleMetadataV8 extends Struct { * The runtime metadata as a decoded structure */ export default class MetadataV8 extends Struct implements MetadataInterface { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { modules: Vec.with(ModuleMetadataV8) }, value); } diff --git a/packages/rpc-core/src/cached.spec.ts b/packages/rpc-core/src/cached.spec.ts index b73b8a7ab888..cca0ad860669 100644 --- a/packages/rpc-core/src/cached.spec.ts +++ b/packages/rpc-core/src/cached.spec.ts @@ -4,16 +4,17 @@ import testingPairs from '@polkadot/keyring/testingPairs'; import MockProvider from '@polkadot/rpc-provider/mock'; -import { createType } from '@polkadot/types'; +import { createType, TypeRegistry } from '@polkadot/types'; import Rpc from '.'; describe('Cached Observables', (): void => { + const registry = new TypeRegistry(); let rpc: Rpc; const keyring = testingPairs(); beforeEach((): void => { - rpc = new Rpc(new MockProvider()); + rpc = new Rpc(registry, new MockProvider(registry)); }); it('creates a single observable for subscriptions (multiple calls)', (): void => { @@ -32,7 +33,7 @@ describe('Cached Observables', (): void => { it('creates a single observable (multiple calls, different arguments that should be cached together)', (): void => { const observable1 = rpc.state.subscribeStorage([keyring.alice.address]); - const observable2 = rpc.state.subscribeStorage([createType('AccountId', keyring.alice.address)]); + const observable2 = rpc.state.subscribeStorage([createType(registry, 'AccountId', keyring.alice.address)]); expect(observable2).toBe(observable1); }); diff --git a/packages/rpc-core/src/formatting.spec.ts b/packages/rpc-core/src/formatting.spec.ts index 011d32351544..eef2023108c3 100644 --- a/packages/rpc-core/src/formatting.spec.ts +++ b/packages/rpc-core/src/formatting.spec.ts @@ -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 '@polkadot/types/injector'; - import { Balance } from '@polkadot/types/interfaces'; import { Codec } from '@polkadot/types/types'; @@ -14,9 +12,11 @@ import rpcMetadataV5 from '@polkadot/metadata/Metadata/v5/static'; import rpcMetadataV6 from '@polkadot/metadata/Metadata/v6/static'; import rpcMetadataV7 from '@polkadot/metadata/Metadata/v7/static'; import rpcMetadataV8 from '@polkadot/metadata/Metadata/v8/static'; +import { TypeRegistry } from '@polkadot/types'; -import Api from '.'; +import Rpc from '.'; +const registry = new TypeRegistry(); const ADDR_ONE = '5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo'; const ADDR_TWO = '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'; const BALANCE_KEYS = [ @@ -29,7 +29,7 @@ function formattingTests (version: string, decorated: Metadata, encodedValues: [ const [ENC_ONE, ENC_TWO, CONTRACT_KEY] = encodedValues; describe(`formatting with Metadata ${version}`, (): void => { - let api: Api; + let api: Rpc; let provider: any; beforeEach((): void => { @@ -73,7 +73,7 @@ function formattingTests (version: string, decorated: Metadata, encodedValues: [ }) }; - api = new Api(provider); + api = new Rpc(registry, provider); }); it('encodes key (with params), decoding response', (done): void => { @@ -182,37 +182,37 @@ function formattingTests (version: string, decorated: Metadata, encodedValues: [ }); } -formattingTests('v3', new Metadata(rpcMetadataV3), [ +formattingTests('v3', new Metadata(registry, rpcMetadataV3), [ '0x4af2c53fce3ec33c6ccccf22e926f1a7', '0x3e62f7ed6e788e1337bce2a97b68a12a', '0x777519cd81f845abdb40d253923d6098' ]); -formattingTests('v4', new Metadata(rpcMetadataV4), [ +formattingTests('v4', new Metadata(registry, rpcMetadataV4), [ '0xec8f96437274a883afcac82d01a9defeb68209cd4f2c084632813692aa5e65ad', '0x1dbb0224910f42a14e7f1406b24c6fe8157296691b02a78756e01946038fffab', '0xc7879f4faa637a90d782070a3cb6be99a9fb0316e19a0454ce93c4f0a34712f1' ]); -formattingTests('v5', new Metadata(rpcMetadataV5), [ +formattingTests('v5', new Metadata(registry, rpcMetadataV5), [ '0xec8f96437274a883afcac82d01a9defeb68209cd4f2c084632813692aa5e65ad', '0x1dbb0224910f42a14e7f1406b24c6fe8157296691b02a78756e01946038fffab', '0xc7879f4faa637a90d782070a3cb6be99a9fb0316e19a0454ce93c4f0a34712f1' ]); -formattingTests('v6', new Metadata(rpcMetadataV6), [ +formattingTests('v6', new Metadata(registry, rpcMetadataV6), [ '0xec8f96437274a883afcac82d01a9defeb68209cd4f2c084632813692aa5e65ad', '0x1dbb0224910f42a14e7f1406b24c6fe8157296691b02a78756e01946038fffab', '0xc7879f4faa637a90d782070a3cb6be99a9fb0316e19a0454ce93c4f0a34712f1' ]); -formattingTests('v7', new Metadata(rpcMetadataV7), [ +formattingTests('v7', new Metadata(registry, rpcMetadataV7), [ '0xec8f96437274a883afcac82d01a9defeb68209cd4f2c084632813692aa5e65ad', '0x1dbb0224910f42a14e7f1406b24c6fe8157296691b02a78756e01946038fffab', '0xc7879f4faa637a90d782070a3cb6be99a9fb0316e19a0454ce93c4f0a34712f1' ]); -formattingTests('v8', new Metadata(rpcMetadataV8), [ +formattingTests('v8', new Metadata(registry, rpcMetadataV8), [ '0xec8f96437274a883afcac82d01a9defeb68209cd4f2c084632813692aa5e65ad', '0x1dbb0224910f42a14e7f1406b24c6fe8157296691b02a78756e01946038fffab', '0xc7879f4faa637a90d782070a3cb6be99a9fb0316e19a0454ce93c4f0a34712f1' diff --git a/packages/rpc-core/src/index.spec.ts b/packages/rpc-core/src/index.spec.ts index 0e8d0212e0a5..48a66ae24123 100644 --- a/packages/rpc-core/src/index.spec.ts +++ b/packages/rpc-core/src/index.spec.ts @@ -3,30 +3,33 @@ // of the Apache-2.0 license. See the LICENSE file for details. import MockProvider from '@polkadot/rpc-provider/mock'; +import { TypeRegistry } from '@polkadot/types'; import { isFunction } from '@polkadot/util'; import Rpc from '.'; describe('Api', (): void => { + const registry = new TypeRegistry(); + it('requires a provider with a send method', (): void => { expect( - (): Rpc => new Rpc({} as any) + (): Rpc => new Rpc(registry, {} as any) ).toThrow(/Expected Provider/); }); it('creates an instance with all sections', (): void => { - const rpc = new Rpc(new MockProvider()); + const rpc = new Rpc(registry, new MockProvider(registry)); expect( Object.keys(rpc).filter((key): boolean => !key.startsWith('_')) ).toEqual([ - 'provider', 'mapping', 'sections', + 'mapping', 'provider', 'registry', 'sections', 'account', 'author', 'chain', 'contracts', 'payment', 'rpc', 'state', 'system' ]); }); it('allows for the definition of user RPCs', (): void => { - const rpc = new Rpc(new MockProvider(), { + const rpc = new Rpc(registry, new MockProvider(registry), { testing: [ { name: 'foo', diff --git a/packages/rpc-core/src/index.ts b/packages/rpc-core/src/index.ts index c40a6e21d184..acc83e85eb4e 100644 --- a/packages/rpc-core/src/index.ts +++ b/packages/rpc-core/src/index.ts @@ -4,7 +4,7 @@ import { ProviderInterface } from '@polkadot/rpc-provider/types'; import { RpcMethod, RpcSection, RpcParam } from '@polkadot/jsonrpc/types'; -import { AnyJson, Codec } from '@polkadot/types/types'; +import { AnyJson, Codec, Registry } from '@polkadot/types/types'; import { RpcInterface } from './jsonrpc.types'; import { RpcInterfaceMethod, UserRpc } from './types'; @@ -68,9 +68,11 @@ function createErrorMessage ({ method, params, type }: RpcMethod, error: Error): export default class Rpc implements RpcInterface { private _storageCache = new Map(); + public readonly mapping: Map = new Map(); + public readonly provider: ProviderInterface; - public readonly mapping: Map = new Map(); + public readonly registry: Registry; public readonly sections: string[] = []; @@ -99,10 +101,11 @@ export default class Rpc implements RpcInterface { * Default constructor for the Api Object * @param {ProviderInterface} provider An API provider using HTTP or WebSocket */ - constructor (provider: ProviderInterface, userRpc: UserRpc = {}) { + constructor (registry: Registry, provider: ProviderInterface, userRpc: UserRpc = {}) { // eslint-disable-next-line @typescript-eslint/unbound-method assert(provider && isFunction(provider.send), 'Expected Provider to API create'); + this.registry = registry; this.provider = provider; this.createInterfaces(jsonrpc, userRpc); @@ -307,7 +310,7 @@ 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 => - createTypeUnsafe(method.params[index].type, [input]) + createTypeUnsafe(this.registry, method.params[index].type, [input]) ); } @@ -351,7 +354,7 @@ export default class Rpc implements RpcInterface { }, [] as Codec[]); } - return createTypeUnsafe(method.type, [result]); + return createTypeUnsafe(this.registry, method.type, [result]); } private formatStorageData (key: StorageKey, value: string | null): Codec { @@ -371,14 +374,15 @@ export default class Rpc implements RpcInterface { if (meta.modifier.isOptional) { return new Option( - createClass(type), + this.registry, + createClass(this.registry, type), isEmpty ? null - : createTypeUnsafe(type, [input], true) + : createTypeUnsafe(this.registry, type, [input], true) ); } - return createTypeUnsafe(type, [isEmpty ? meta.fallback : input], true); + return createTypeUnsafe(this.registry, type, [isEmpty ? meta.fallback : input], true); } private formatStorageSet (key: StorageKey, changes: [string, string | null][], witCache: boolean): Codec { @@ -412,13 +416,14 @@ export default class Rpc implements RpcInterface { if (meta.modifier.isOptional) { return new Option( - createClass(type), + this.registry, + createClass(this.registry, type), isEmpty ? null - : createTypeUnsafe(type, [input], true) + : createTypeUnsafe(this.registry, type, [input], true) ); } - return createTypeUnsafe(type, [isEmpty ? meta.fallback : input], true); + return createTypeUnsafe(this.registry, type, [isEmpty ? meta.fallback : input], true); } } diff --git a/packages/rpc-core/src/methodSend.spec.ts b/packages/rpc-core/src/methodSend.spec.ts index 3374d74e8c14..7e612f3f1668 100644 --- a/packages/rpc-core/src/methodSend.spec.ts +++ b/packages/rpc-core/src/methodSend.spec.ts @@ -2,9 +2,12 @@ // 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 '@polkadot/types'; + import Rpc from '.'; describe('methodSend', (): void => { + const registry = new TypeRegistry(); let rpc: Rpc; let methods: any; let provider: any; @@ -33,7 +36,7 @@ describe('methodSend', (): void => { }) }; - rpc = new Rpc(provider); + rpc = new Rpc(registry, provider); }); it('wraps errors with the call signature', (done): void => { diff --git a/packages/rpc-core/src/replay.spec.ts b/packages/rpc-core/src/replay.spec.ts index 683a8cad1829..99bbe0f90d1d 100644 --- a/packages/rpc-core/src/replay.spec.ts +++ b/packages/rpc-core/src/replay.spec.ts @@ -4,14 +4,16 @@ import { Observable, of } from 'rxjs'; import MockProvider from '@polkadot/rpc-provider/mock'; +import { TypeRegistry } from '@polkadot/types'; import Rpc from '.'; describe('replay', (): void => { + const registry = new TypeRegistry(); let rpc: Rpc; beforeEach((): void => { - rpc = new Rpc(new MockProvider()); + rpc = new Rpc(registry, new MockProvider(registry)); }); it('subscribes via the rpc section', (done): void => { diff --git a/packages/rpc-provider/src/mock/index.ts b/packages/rpc-provider/src/mock/index.ts index 28f68e223577..73a582704f70 100644 --- a/packages/rpc-provider/src/mock/index.ts +++ b/packages/rpc-provider/src/mock/index.ts @@ -3,10 +3,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '@polkadot/types/injector'; - import { Header } from '@polkadot/types/interfaces'; -import { Codec } from '@polkadot/types/types'; +import { Codec, Registry } from '@polkadot/types/types'; import { ProviderInterface, ProviderInterfaceEmitted, ProviderInterfaceEmitCb } from '../types'; import { MockStateSubscriptions, MockStateSubscriptionCallback, MockStateDb } from './types'; @@ -50,12 +48,14 @@ export default class Mock implements ProviderInterface { public isUpdating = true; + private registry: Registry; + private requests: Record any> = { // eslint-disable-next-line @typescript-eslint/no-unused-vars - chain_getBlock: (hash: string): any => createType('SignedBlock', rpcSignedBlock.result).toJSON(), + chain_getBlock: (hash: string): any => createType(this.registry, 'SignedBlock', rpcSignedBlock.result).toJSON(), // eslint-disable-next-line @typescript-eslint/no-unused-vars chain_getBlockHash: (blockNumber: number): string => '0x1234', - state_getRuntimeVersion: (): string => createType('RuntimeVersion').toHex(), + state_getRuntimeVersion: (): string => createType(this.registry, 'RuntimeVersion').toHex(), state_getStorage: (storage: MockStateDb, params: any[]): string => { return u8aToHex( storage[(params[0] as string)] @@ -81,7 +81,9 @@ export default class Mock implements ProviderInterface { private subscriptionMap: Record = {}; - constructor () { + constructor (registry: Registry) { + this.registry = registry; + this.init(); } @@ -157,7 +159,7 @@ export default class Mock implements ProviderInterface { let newHead = this.makeBlockHeader(new BN(-1)); let counter = -1; - const metadata = new Metadata(rpcMetadata); + const metadata = new Metadata(this.registry, rpcMetadata); // Do something every 1 seconds setInterval((): void => { @@ -192,7 +194,7 @@ export default class Mock implements ProviderInterface { private makeBlockHeader (prevNumber: BN): Header { const blockNumber = prevNumber.addn(1); - return createType('Header', { + return createType(this.registry, 'Header', { digest: { logs: [] }, diff --git a/packages/rpc-provider/src/mock/on.spec.ts b/packages/rpc-provider/src/mock/on.spec.ts index e071e5aa9110..34444bae52db 100644 --- a/packages/rpc-provider/src/mock/on.spec.ts +++ b/packages/rpc-provider/src/mock/on.spec.ts @@ -4,13 +4,16 @@ import { ProviderInterfaceEmitted } from '../types'; +import { TypeRegistry } from '@polkadot/types'; + import Mock from './'; describe('on', (): void => { + const registry = new TypeRegistry(); let mock: Mock; beforeEach((): void => { - mock = new Mock(); + mock = new Mock(registry); }); it('it emits both connected and disconnected events', (done): void => { diff --git a/packages/rpc-provider/src/mock/send.spec.ts b/packages/rpc-provider/src/mock/send.spec.ts index 5afa2e373bfb..0e39c4db62b4 100644 --- a/packages/rpc-provider/src/mock/send.spec.ts +++ b/packages/rpc-provider/src/mock/send.spec.ts @@ -2,13 +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 { TypeRegistry } from '@polkadot/types'; + import Mock from './'; describe('send', (): void => { + const registry = new TypeRegistry(); let mock: Mock; beforeEach((): void => { - mock = new Mock(); + mock = new Mock(registry); }); it('fails on non-supported methods', (): Promise => { diff --git a/packages/rpc-provider/src/mock/subscribe.spec.ts b/packages/rpc-provider/src/mock/subscribe.spec.ts index 7519c85c0bd3..a0e9ccc92f59 100644 --- a/packages/rpc-provider/src/mock/subscribe.spec.ts +++ b/packages/rpc-provider/src/mock/subscribe.spec.ts @@ -2,13 +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 { TypeRegistry } from '@polkadot/types'; + import Mock from './'; describe('subscribe', (): void => { + const registry = new TypeRegistry(); let mock: Mock; beforeEach((): void => { - mock = new Mock(); + mock = new Mock(registry); }); it('fails on unknown methods', (): Promise => { diff --git a/packages/rpc-provider/src/mock/unsubscribe.spec.ts b/packages/rpc-provider/src/mock/unsubscribe.spec.ts index a0d3eacda879..46d172a44380 100644 --- a/packages/rpc-provider/src/mock/unsubscribe.spec.ts +++ b/packages/rpc-provider/src/mock/unsubscribe.spec.ts @@ -2,14 +2,17 @@ // 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 '@polkadot/types'; + import Mock from './'; describe('unsubscribe', (): void => { + const registry = new TypeRegistry(); let mock: Mock; let id: number; beforeEach((): Promise => { - mock = new Mock(); + mock = new Mock(registry); return mock .subscribe('chain_newHead', 'chain_subscribeNewHead', (): void => {}) diff --git a/packages/types/src/codec/AbstractArray.ts b/packages/types/src/codec/AbstractArray.ts index a536e0c5117a..9cc5b897fbc8 100644 --- a/packages/types/src/codec/AbstractArray.ts +++ b/packages/types/src/codec/AbstractArray.ts @@ -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 { AnyJson, AnyJsonArray, Codec, IHash } from '../types'; +import { AnyJson, AnyJsonArray, Codec, IHash, Registry } from '../types'; import { u8aConcat, u8aToHex } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -19,6 +19,14 @@ import { compareArray } from './utils'; * @noInheritDoc */ export default abstract class AbstractArray extends Array implements Codec { + public readonly registry: Registry; + + protected constructor (registry: Registry, ...values: T[]) { + super(...values); + + this.registry = registry; + } + /** * @description The length of the value when encoded as a Uint8Array */ @@ -32,7 +40,7 @@ export default abstract class AbstractArray extends Array im * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/AbstractInt.ts b/packages/types/src/codec/AbstractInt.ts index e33249ae7dd3..d71b951af08d 100644 --- a/packages/types/src/codec/AbstractInt.ts +++ b/packages/types/src/codec/AbstractInt.ts @@ -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 { AnyNumber, Codec, IHash } from '../types'; +import { AnyNumber, Codec, IHash, Registry } from '../types'; import BN from 'bn.js'; import { bnToBn, hexToBn, isHex, isString, isU8a, u8aToBn } from '@polkadot/util'; @@ -22,20 +22,18 @@ export const DEFAULT_UINT_BITS = 64; // TODO: // - Apart from encoding/decoding we don't actually keep check on the sizes, is this good enough? export default abstract class AbstractInt extends BN implements Codec { + public readonly registry: Registry; + protected _bitLength: UIntBitLength; private _isHexJson: boolean; private _isNegative: boolean; - constructor ( - isNegative: boolean, - value: AnyNumber = 0, - bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = true) { - super( - AbstractInt.decodeAbstracInt(value, bitLength, isNegative) - ); + protected constructor (registry: Registry, isNegative: boolean, value: AnyNumber = 0, bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = true) { + super(AbstractInt.decodeAbstracInt(value, bitLength, isNegative)); + this.registry = registry; this._bitLength = bitLength; this._isHexJson = isHexJson; this._isNegative = isNegative; @@ -80,7 +78,7 @@ export default abstract class AbstractInt extends BN implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/BTreeMap.spec.ts b/packages/types/src/codec/BTreeMap.spec.ts index e694d0f0ce9b..e76484f5e5b9 100644 --- a/packages/types/src/codec/BTreeMap.spec.ts +++ b/packages/types/src/codec/BTreeMap.spec.ts @@ -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 '../injector'; +import { TypeRegistry } from '../codec'; import Text from '../primitive/Text'; import U32 from '../primitive/U32'; import Struct from './Struct'; @@ -10,18 +10,19 @@ import { CodecTo } from '../types'; import BTreeMap from './BTreeMap'; +const registry = new TypeRegistry(); const mockU32TextMap = new Map(); -mockU32TextMap.set(new Text('bazzing'), new U32(69)); +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(); -mockU32U32Map.set(new U32(1), new U32(2)); -mockU32U32Map.set(new U32(23), new U32(24)); -mockU32U32Map.set(new U32(28), new U32(30)); -mockU32U32Map.set(new U32(45), new U32(80)); +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'; @@ -31,7 +32,7 @@ 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(Text, U32, input); + const s = new BTreeMap(registry, Text, U32, input); expect(s.toString()).toBe(output); }); @@ -48,7 +49,7 @@ describe('BTreeMap', (): void => { describe('encoding', (): void => { const testEncode = (to: CodecTo, expected: any): void => it(`can encode ${to}`, (): void => { - const s = new BTreeMap(Text, U32, mockU32TextMap); + const s = new BTreeMap(registry, Text, U32, mockU32TextMap); expect(s[to]()).toEqual(expected); }); @@ -61,7 +62,7 @@ describe('BTreeMap', (): void => { describe('encoding muple values', (): void => { const testEncode = (to: CodecTo, expected: any): void => it(`can encode ${to}`, (): void => { - const s = new BTreeMap(Text, U32, mockU32U32Map); + const s = new BTreeMap(registry, Text, U32, mockU32U32Map); expect(s[to]()).toEqual(expected); }); @@ -75,16 +76,16 @@ describe('BTreeMap', (): void => { expect( new ( BTreeMap.with(Text, U32) - )(null).toString() + )(registry, null).toString() ).toEqual('{}'); }); it('decodes within more complicated types', (): void => { - const s = new Struct({ + const s = new Struct(registry, { placeholder: U32, value: BTreeMap.with(Text, U32) }); - s.set('value', new BTreeMap(Text, U32, mockU32TextMap)); + s.set('value', new BTreeMap(registry, Text, U32, mockU32TextMap)); expect(s.toString()).toBe('{"placeholder":0,"value":{"bazzing":69}}'); }); @@ -92,21 +93,21 @@ describe('BTreeMap', (): void => { expect( (): BTreeMap => new ( BTreeMap.with(Text, U32) - )('ABC') + )(registry, 'ABC') ).toThrowError(/BTreeMap: cannot decode type/); }); it('correctly encodes length', (): void => { expect( new ( - BTreeMap.with(Text, U32))(mockU32TextMap).encodedLength + BTreeMap.with(Text, U32))(registry, mockU32TextMap).encodedLength ).toEqual(13); }); it('generates sane toRawTypes', (): void => { - expect(new (BTreeMap.with(Text, U32))().toRawType()).toBe('BTreeMap'); - expect(new (BTreeMap.with(Text, Text))().toRawType()).toBe('BTreeMap'); - expect(new (BTreeMap.with(Text, Struct.with({ a: U32, b: Text })))().toRawType()) + expect(new (BTreeMap.with(Text, U32))(registry).toRawType()).toBe('BTreeMap'); + expect(new (BTreeMap.with(Text, Text))(registry).toRawType()).toBe('BTreeMap'); + expect(new (BTreeMap.with(Text, Struct.with({ a: U32, b: Text })))(registry).toRawType()) .toBe('BTreeMap'); }); }); diff --git a/packages/types/src/codec/BTreeMap.ts b/packages/types/src/codec/BTreeMap.ts index 38051f611f4a..79a492b7f436 100644 --- a/packages/types/src/codec/BTreeMap.ts +++ b/packages/types/src/codec/BTreeMap.ts @@ -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 { AnyJson, Constructor, Codec, IHash, InterfaceTypes } from '../types'; +import { AnyJson, Constructor, Codec, IHash, InterfaceTypes, Registry } from '../types'; import { isHex, hexToU8a, isU8a, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -12,16 +12,19 @@ import U8a from './U8a'; import { compareMap, decodeU8a, typeToConstructor } from './utils'; export default class BTreeMap extends Map implements Codec { + public readonly registry: Registry; + protected _KeyClass: Constructor; protected _ValClass: Constructor; - constructor (keyType: Constructor | InterfaceTypes, valType: Constructor | InterfaceTypes, rawValue: any) { - const KeyClass = typeToConstructor(keyType); - const ValClass = typeToConstructor(valType); + constructor (registry: Registry, keyType: Constructor | InterfaceTypes, valType: Constructor | InterfaceTypes, rawValue: any) { + const KeyClass = typeToConstructor(registry, keyType); + const ValClass = typeToConstructor(registry, valType); - super(BTreeMap.decodeBTreeMap(KeyClass, ValClass, rawValue)); + super(BTreeMap.decodeBTreeMap(registry, KeyClass, ValClass, rawValue)); + this.registry = registry; this._KeyClass = KeyClass; this._ValClass = ValClass; } @@ -40,21 +43,21 @@ export default class BTreeMap * constructors or decodeable values for their types. * @param jsonMap */ - private static decodeBTreeMap (KeyClass: Constructor, ValClass: Constructor, value: Uint8Array | string | Map): Map { + private static decodeBTreeMap (registry: Registry, KeyClass: Constructor, ValClass: Constructor, value: Uint8Array | string | Map): Map { if (!value) { return new Map(); } else if (isHex(value)) { - return BTreeMap.decodeBTreeMap(KeyClass, ValClass, hexToU8a(value)); + return BTreeMap.decodeBTreeMap(registry, KeyClass, ValClass, hexToU8a(value)); } else if (isU8a(value)) { - return BTreeMap.decodeBTreeMapFromU8a(KeyClass, ValClass, u8aToU8a(value)); + return BTreeMap.decodeBTreeMapFromU8a(registry, KeyClass, ValClass, u8aToU8a(value)); } else if (value instanceof Map) { - return BTreeMap.decodeBTreeMapFromMap(KeyClass, ValClass, value); + return BTreeMap.decodeBTreeMapFromMap(registry, KeyClass, ValClass, value); } throw new Error('BTreeMap: cannot decode type'); } - private static decodeBTreeMapFromU8a (KeyClass: Constructor, ValClass: Constructor, u8a: Uint8Array): Map { + private static decodeBTreeMapFromU8a (registry: Registry, KeyClass: Constructor, ValClass: Constructor, u8a: Uint8Array): Map { const output = new Map(); const [offset, length] = Compact.decodeU8a(u8a); const types = []; @@ -63,7 +66,7 @@ export default class BTreeMap types.push(KeyClass, ValClass); } - const values = decodeU8a(u8a.subarray(offset), types); + 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); @@ -72,14 +75,14 @@ export default class BTreeMap return output; } - private static decodeBTreeMapFromMap (KeyClass: Constructor, ValClass: Constructor, value: Map): Map { + private static decodeBTreeMapFromMap (registry: Registry, KeyClass: Constructor, ValClass: Constructor, value: Map): Map { const output = new Map(); value.forEach((v: any, k: any) => { let key, val; try { - key = (k instanceof KeyClass) ? k : new KeyClass(k); - val = (v instanceof ValClass) ? v : new ValClass(v); + key = (k instanceof KeyClass) ? k : new KeyClass(registry, k); + val = (v instanceof ValClass) ? v : new ValClass(registry, v); } catch (error) { console.error('Failed to decode BTreeMap key or value:', error.message); throw error; @@ -93,8 +96,8 @@ export default class BTreeMap public static with (keyType: Constructor | InterfaceTypes, valType: Constructor | InterfaceTypes): Constructor> { return class extends BTreeMap { - constructor (value?: any) { - super(keyType, valType, value); + constructor (registry: Registry, value?: any) { + super(registry, keyType, valType, value); } }; } @@ -116,7 +119,7 @@ export default class BTreeMap * @description Returns a hash of the value */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** @@ -157,7 +160,7 @@ export default class BTreeMap * @description Returns the base runtime type name for this instance */ public toRawType (): string { - return `BTreeMap<${new this._KeyClass().toRawType()},${new this._ValClass().toRawType()}>`; + return `BTreeMap<${new this._KeyClass(this.registry).toRawType()},${new this._ValClass(this.registry).toRawType()}>`; } /** diff --git a/packages/types/src/codec/Base.ts b/packages/types/src/codec/Base.ts index da41caa2ff8c..9bbc6e0d0120 100644 --- a/packages/types/src/codec/Base.ts +++ b/packages/types/src/codec/Base.ts @@ -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 { AnyJson, Codec, IHash } from '../types'; +import { AnyJson, Codec, IHash, Registry } from '../types'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -13,9 +13,12 @@ import U8a from './U8a'; * @description A type extends the Base class, when it holds a value */ export default abstract class Base implements Codec { + public readonly registry: Registry; + protected raw: T; - constructor (value?: any) { + protected constructor (registry: Registry, value?: any) { + this.registry = registry; this.raw = value; } @@ -30,7 +33,7 @@ export default abstract class Base implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/Compact.spec.ts b/packages/types/src/codec/Compact.spec.ts index 17f718ff658e..9d7dce75cfbc 100644 --- a/packages/types/src/codec/Compact.spec.ts +++ b/packages/types/src/codec/Compact.spec.ts @@ -2,17 +2,17 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../injector'; - import BN from 'bn.js'; -import { createType, ClassOf } from './create'; +import { createType, ClassOf, TypeRegistry } from './create'; import Moment from './Date'; import U32 from '../primitive/U32'; import Compact from './Compact'; import UInt from './UInt'; describe('Compact', (): void => { + const registry = new TypeRegistry(); + describe('encodeU8a', (): void => { it('encodes short u8', (): void => { expect( @@ -24,7 +24,7 @@ describe('Compact', (): void => { it('encodes max u8 values', (): void => { expect( - Compact.encodeU8a(new UInt(63)) + Compact.encodeU8a(new (UInt.with())(registry, 63)) ).toEqual( new Uint8Array([0b11111100]) ); @@ -64,7 +64,7 @@ describe('Compact', (): void => { it('encondes a large balance', (): void => { expect( - Compact.encodeU8a(createType('Balance', '0x5af3107a4000')) + Compact.encodeU8a(createType(registry, 'Balance', '0x5af3107a4000')) ).toEqual( new Uint8Array([ 3 + ((6 - 4) << 2), @@ -109,52 +109,52 @@ describe('Compact', (): void => { describe('constructor', (): void => { it('has the correct bitLength for constructor values (BlockNumber)', (): void => { expect( - new Compact(ClassOf('BlockNumber'), 0xfffffff9).bitLength() + new (Compact.with(ClassOf(registry, 'BlockNumber')))(registry, 0xfffffff9).bitLength() ).toEqual(32); }); it('has the correct encodedLength for constructor values (string BlockNumber)', (): void => { expect( - new Compact('BlockNumber', 0xfffffff9).encodedLength + new (Compact.with('BlockNumber'))(registry, 0xfffffff9).encodedLength ).toEqual(5); }); it('has the correct encodedLength for constructor values (class BlockNumber)', (): void => { expect( - new Compact(ClassOf('BlockNumber'), 0xfffffff9).encodedLength + new (Compact.with(ClassOf(registry, 'BlockNumber')))(registry, 0xfffffff9).encodedLength ).toEqual(5); }); it('has the correct encodedLength for constructor values (u32)', (): void => { expect( - new Compact(U32, 0xffff9).encodedLength + new (Compact.with(U32))(registry, 0xffff9).encodedLength ).toEqual(4); }); it('constructs properly via U8a as U32', (): void => { expect( - new Compact(U32, new Uint8Array([254, 255, 3, 0])).toNumber() + new (Compact.with(U32))(registry, new Uint8Array([254, 255, 3, 0])).toNumber() ).toEqual(new BN(0xffff).toNumber()); }); it('constructs properly via number as Moment', (): void => { expect( - new Compact(Moment, 1537968546).toString().startsWith('Wed Sep 26 2018') // The time depends on the timezone this test is run in + new (Compact.with(Moment))(registry, 1537968546).toString().startsWith('Wed Sep 26 2018') // The time depends on the timezone this test is run in ).toBe(true); }); }); describe('utils', (): void => { it('compares against another Compact', (): void => { - expect(new Compact(U32, 12345).eq(new Compact(U32, 12345))).toBe(true); + expect(new (Compact.with(U32))(registry, 12345).eq(new (Compact.with(U32))(registry, 12345))).toBe(true); }); it('compares against a primitive', (): void => { - expect(new Compact(U32, 12345).eq(12345)).toBe(true); + expect(new (Compact.with(U32))(registry, 12345).eq(12345)).toBe(true); }); it('unwraps to the wrapped value', (): void => { - expect(new Compact(U32, 12345).unwrap() instanceof U32).toBe(true); + expect(new (Compact.with(U32))(registry, 12345).unwrap() instanceof U32).toBe(true); }); }); diff --git a/packages/types/src/codec/Compact.ts b/packages/types/src/codec/Compact.ts index 736523f5118e..0e9d5a575b9e 100644 --- a/packages/types/src/codec/Compact.ts +++ b/packages/types/src/codec/Compact.ts @@ -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 { AnyNumber, Codec, Constructor, InterfaceTypes } from '../types'; +import { AnyNumber, Codec, Constructor, InterfaceTypes, Registry } from '../types'; import BN from 'bn.js'; import { compactAddLength, compactFromU8a, compactStripLength, compactToU8a, isBn, isNumber, isString } from '@polkadot/util'; @@ -27,14 +27,14 @@ export interface CompactEncodable extends Codec { * a number and making the compact representation thereof */ export default class Compact extends Base { - constructor (Type: Constructor | InterfaceTypes, value: Compact | AnyNumber = 0) { - super(Compact.decodeCompact(typeToConstructor(Type), value)); + constructor (registry: Registry, Type: Constructor | InterfaceTypes, value: Compact | AnyNumber = 0) { + super(registry, Compact.decodeCompact(registry, typeToConstructor(registry, Type), value)); } public static with (Type: Constructor | InterfaceTypes): Constructor> { return class extends Compact { - constructor (value?: any) { - super(Type, value); + constructor (registry: Registry, value?: any) { + super(registry, Type, value); } }; } @@ -56,16 +56,16 @@ export default class Compact extends Base { return value; } - public static decodeCompact (Type: Constructor, value: Compact | AnyNumber): CompactEncodable { + public static decodeCompact (registry: Registry, Type: Constructor, value: Compact | AnyNumber): CompactEncodable { if (value instanceof Compact) { - return new Type(value.raw); + return new Type(registry, value.raw); } else if (isString(value) || isNumber(value) || isBn(value)) { - return new Type(value); + return new Type(registry, value); } - const [, _value] = Compact.decodeU8a(value, new Type(0).bitLength()); + const [, _value] = Compact.decodeU8a(value, new Type(registry, 0).bitLength()); - return new Type(_value); + return new Type(registry, _value); } /** diff --git a/packages/types/src/codec/Date.spec.ts b/packages/types/src/codec/Date.spec.ts index 33afb75b981f..69eacccc57d1 100644 --- a/packages/types/src/codec/Date.spec.ts +++ b/packages/types/src/codec/Date.spec.ts @@ -2,29 +2,33 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { CodecTo } from '../types'; + import BN from 'bn.js'; -import { CodecTo } from '../types'; -import CodecDate from './Date'; +import { TypeRegistry } from './create'; import U64 from '../primitive/U64'; +import CodecDate from './Date'; describe('Date', (): void => { + const registry = new TypeRegistry(); + describe('decode', (): void => { const testDecode = (type: string, input: Date | CodecDate | U64 | number, expected: string | number, toJSON = false): void => it(`can decode from ${type}`, (): void => { - expect(new CodecDate(input)[toJSON ? 'toJSON' : 'toISOString']()).toBe(expected); + expect(new CodecDate(registry, input)[toJSON ? 'toJSON' : 'toISOString']()).toBe(expected); }); testDecode('Date', new Date(1537968546280), '2018-09-26T13:29:06.280Z'); - testDecode('CodecDate', new CodecDate(1234), 1234, true); + testDecode('CodecDate', new CodecDate(registry, 1234), 1234, true); testDecode('number', 1234, 1234, true); - testDecode('U64', new U64(69), 69, true); + testDecode('U64', new U64(registry, 69), 69, true); }); describe('encode', (): void => { const testEncode = (to: 'toBn' | 'toISOString' | 'toNumber' | CodecTo, expected: BN | number | string | Uint8Array): void => it(`can encode ${to}`, (): void => { - expect(new CodecDate(421)[to]()).toEqual(expected); + expect(new CodecDate(registry, 421)[to]()).toEqual(expected); }); testEncode('toBn', new BN(421)); @@ -36,7 +40,7 @@ describe('Date', (): void => { it('can encode toString', (): void => { expect( - new CodecDate(421) + new CodecDate(registry, 421) .toString() .startsWith('Thu Jan 01 1970') // The time depends on the timezone this test is run in ).toBe(true); @@ -44,24 +48,24 @@ describe('Date', (): void => { it('encodes default BE hex', (): void => { expect( - new CodecDate(3).toHex() + new CodecDate(registry, 3).toHex() ).toEqual('0x0000000000000003'); }); it('encodes options LE hex', (): void => { expect( - new CodecDate(3).toHex(true) + new CodecDate(registry, 3).toHex(true) ).toEqual('0x0300000000000000'); }); }); describe('utils', (): void => { it('compares values', (): void => { - expect(new CodecDate(123).eq(123)).toBe(true); + expect(new CodecDate(registry, 123).eq(123)).toBe(true); }); it('compares values (non-match)', (): void => { - expect(new CodecDate(123).eq(456)).toBe(false); + expect(new CodecDate(registry, 123).eq(456)).toBe(false); }); }); }); diff --git a/packages/types/src/codec/Date.ts b/packages/types/src/codec/Date.ts index af56935e56e6..9cab399aa1a0 100644 --- a/packages/types/src/codec/Date.ts +++ b/packages/types/src/codec/Date.ts @@ -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 { AnyNumber, Codec, IHash } from '../types'; +import { AnyNumber, Codec, IHash, Registry } from '../types'; import BN from 'bn.js'; import { bnToBn, bnToHex, bnToU8a, isString, isU8a, u8aToBn } from '@polkadot/util'; @@ -24,13 +24,14 @@ const BITLENGTH: UIntBitLength = 64; * @noInheritDoc */ export default class CodecDate extends Date implements Codec { + public readonly registry: Registry; + protected raw: Date; // FIXME Remove this once we convert all types out of Base - constructor (value: CodecDate | Date | AnyNumber = 0) { - super( - CodecDate.decodeDate(value) - ); + constructor (registry: Registry, value: CodecDate | Date | AnyNumber = 0) { + super(CodecDate.decodeDate(value)); + this.registry = registry; this.raw = this; } @@ -59,7 +60,7 @@ export default class CodecDate extends Date implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return createType('Hash', blake2AsU8a(this.toU8a(), 256)); + return createType(this.registry, 'Hash', blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/Enum.spec.ts b/packages/types/src/codec/Enum.spec.ts index cf2ef7561d76..6aa6eef66ed5 100644 --- a/packages/types/src/codec/Enum.spec.ts +++ b/packages/types/src/codec/Enum.spec.ts @@ -2,20 +2,24 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../injector'; +import { Registry } from '../types'; import { u8aToHex } from '@polkadot/util'; -import Enum from './Enum'; +import { TypeRegistry } from './create'; import Null from '../primitive/Null'; import Text from '../primitive/Text'; import U32 from '../primitive/U32'; +import Enum from './Enum'; describe('Enum', (): void => { + const registry = new TypeRegistry(); + describe('typed enum (previously EnumType)', (): void => { it('provides a clean toString() (value)', (): void => { expect( new Enum( + registry, { Text, U32 }, new Uint8Array([0, 2 << 2, 49, 50]) ).value.toString() @@ -25,6 +29,7 @@ describe('Enum', (): void => { it('provides a clean toString() (enum)', (): void => { expect( new Enum( + registry, { Text, U32 }, new Uint8Array([1, 2 << 2, 49, 50]) ).toString() @@ -34,6 +39,7 @@ describe('Enum', (): void => { it('decodes from a JSON input (lowercase)', (): void => { expect( new Enum( + registry, { Text, U32 }, { text: 'some text value' } ).value.toString() @@ -43,6 +49,7 @@ describe('Enum', (): void => { it('decodes from hex', (): void => { expect( new Enum( + registry, { Text, U32 }, '0x0134120000' ).value.toString() @@ -52,6 +59,7 @@ describe('Enum', (): void => { it('decodes from hex (string types)', (): void => { expect( new Enum( + registry, { foo: 'Text', bar: 'u32' }, '0x0134120000' ).value.toString() @@ -61,6 +69,7 @@ describe('Enum', (): void => { it('decodes from a JSON input (mixed case)', (): void => { expect( new Enum( + registry, { Text, U32 }, { U32: 42 } ).value.toString() @@ -70,6 +79,7 @@ describe('Enum', (): void => { it('decodes from JSON string', (): void => { expect( new Enum( + registry, { Null, U32 }, 'null' ).type @@ -77,7 +87,7 @@ describe('Enum', (): void => { }); it('has correct isXyz/asXyz (Enum.with)', (): void => { - const test = new (Enum.with({ First: Text, Second: U32, Third: U32 }))({ Second: 42 }) as any as { isSecond: boolean; asSecond: U32; asThird: never }; + const test = new (Enum.with({ First: Text, Second: U32, Third: U32 }))(registry, { Second: 42 }) as any as { isSecond: boolean; asSecond: U32; asThird: never }; expect(test.isSecond).toEqual(true); expect(test.asSecond.toNumber()).toEqual(42); @@ -89,8 +99,8 @@ describe('Enum', (): void => { class B extends Null { } class C extends Null { } class Test extends Enum { - constructor (value?: string, index?: number) { - super({ + constructor (registry: Registry, value?: string, index?: number) { + super(registry, { a: A, b: B, c: C @@ -98,7 +108,7 @@ describe('Enum', (): void => { } } - expect(new Test().toJSON()).toEqual({ a: null }); + expect(new Test(registry).toJSON()).toEqual({ a: null }); }); it('creates via with', (): void => { @@ -107,13 +117,14 @@ describe('Enum', (): void => { class C extends Null { } const Test = Enum.with({ A, B, C }); - expect(new Test().toJSON()).toEqual({ A: null }); - expect(new Test(1234, 1).toJSON()).toEqual({ B: 1234 }); + expect(new Test(registry).toJSON()).toEqual({ A: null }); + expect(new Test(registry, 1234, 1).toJSON()).toEqual({ B: 1234 }); }); it('allows accessing the type and value', (): void => { - const text = new Text('foo'); + const text = new Text(registry, 'foo'); const enumType = new Enum( + registry, { Text, U32 }, { Text: text } ); @@ -125,7 +136,7 @@ describe('Enum', (): void => { describe('utils', (): void => { const DEF = { num: U32, str: Text }; const u8a = new Uint8Array([1, 3 << 2, 88, 89, 90]); - const test = new Enum(DEF, u8a); + const test = new Enum(registry, DEF, u8a); it('compares against index', (): void => { expect(test.eq(1)).toBe(true); @@ -140,7 +151,7 @@ describe('Enum', (): void => { }); it('compares against another enum', (): void => { - expect(test.eq(new Enum(DEF, u8a))).toBe(true); + expect(test.eq(new Enum(registry, DEF, u8a))).toBe(true); }); it('compares against another object', (): void => { @@ -152,7 +163,7 @@ describe('Enum', (): void => { }); it('compares basic enum on string', (): void => { - expect(new Enum(['A', 'B', 'C'], 1).eq('B')).toBe(true); + expect(new Enum(registry, ['A', 'B', 'C'], 1).eq('B')).toBe(true); }); }); }); @@ -160,20 +171,20 @@ describe('Enum', (): void => { describe('string-only construction (old Enum)', (): void => { const testDecode = (type: string, input: any, expected: any): void => it(`can decode from ${type}`, (): void => { - const e = new Enum(['foo', 'bar'], input); + const e = new Enum(registry, ['foo', 'bar'], input); expect(e.toString()).toBe(expected); }); const testEncode = (to: 'toJSON' | 'toNumber' | 'toString' | 'toU8a', expected: any): void => it(`can encode ${to}`, (): void => { - const e = new Enum(['foo', 'bar'], 1); + const e = new Enum(registry, ['foo', 'bar'], 1); expect(e[to]()).toEqual(expected); }); testDecode('Enum', undefined, 'foo'); - testDecode('Enum', new Enum(['foo', 'bar'], 1), 'bar'); + testDecode('Enum', new Enum(registry, ['foo', 'bar'], 1), 'bar'); testDecode('number', 0, 'foo'); testDecode('number', 1, 'bar'); testDecode('string', 'bar', 'bar'); @@ -187,61 +198,61 @@ describe('Enum', (): void => { it('provides a clean toString()', (): void => { expect( - new Enum(['foo', 'bar']).toString() + new Enum(registry, ['foo', 'bar']).toString() ).toEqual('foo'); }); it('provides a clean toString() (enum)', (): void => { expect( - new Enum(['foo', 'bar'], new Enum(['foo', 'bar'], 1)).toNumber() + new Enum(registry, ['foo', 'bar'], new Enum(registry, ['foo', 'bar'], 1)).toNumber() ).toEqual(1); }); it('converts to and from U8a', (): void => { expect( - new Enum(['foo', 'bar'], new Uint8Array([1])).toU8a() + new Enum(registry, ['foo', 'bar'], new Uint8Array([1])).toU8a() ).toEqual(new Uint8Array([1])); }); it('converts from JSON', (): void => { expect( - new Enum(['foo', 'bar', 'baz', 'gaz', 'jaz'], 4).toNumber() + new Enum(registry, ['foo', 'bar', 'baz', 'gaz', 'jaz'], 4).toNumber() ).toEqual(4); }); it('has correct isXyz getters (Enum.with)', (): void => { - const test = new (Enum.with(['First', 'Second', 'Third']))('Second') as any as { isSecond: boolean; asSecond: never }; + const test = new (Enum.with(['First', 'Second', 'Third']))(registry, 'Second') as any as { isSecond: boolean; asSecond: never }; expect(test.isSecond).toEqual(true); }); describe('utils', (): void => { - it('compares agains the index value', (): void => { + it('compares against the index value', (): void => { expect( - new Enum(['foo', 'bar'], 1).eq(1) + new Enum(registry, ['foo', 'bar'], 1).eq(1) ).toBe(true); }); - it('compares agains the index value (false)', (): void => { + it('compares against the index value (false)', (): void => { expect( - new Enum(['foo', 'bar'], 1).eq(0) + new Enum(registry, ['foo', 'bar'], 1).eq(0) ).toBe(false); }); - it('compares agains the string value', (): void => { + it('compares against the string value', (): void => { expect( - new Enum(['foo', 'bar'], 1).eq('bar') + new Enum(registry, ['foo', 'bar'], 1).eq('bar') ).toBe(true); }); - it('compares agains the string value (false)', (): void => { + it('compares against the string value (false)', (): void => { expect( - new Enum(['foo', 'bar'], 1).eq('foo') + new Enum(registry, ['foo', 'bar'], 1).eq('foo') ).toBe(false); }); it('has isNone set, with correct index (i.e. no values are used)', (): void => { - const test = new Enum(['foo', 'bar'], 1); + const test = new Enum(registry, ['foo', 'bar'], 1); expect(test.isNone).toBe(true); expect(test.index).toEqual(1); @@ -255,7 +266,7 @@ describe('Enum', (): void => { A: U32, B: U32 }); - const test = new Test(new U32(123), 1); + const test = new Test(registry, new U32(registry, 123), 1); expect(test.type).toEqual('B'); expect((test.value as U32).toNumber()).toEqual(123); @@ -266,7 +277,7 @@ describe('Enum', (): void => { A: U32, B: U32 }); - const test = new Test(new Test(123, 1)); + const test = new Test(registry, new Test(registry, 123, 1)); expect(test.type).toEqual('B'); expect((test.value as U32).toNumber()).toEqual(123); @@ -281,7 +292,7 @@ describe('Enum', (): void => { A: U32, B: Nest }); - const test = new Test(new Nest(123, 1), 1); + const test = new Test(registry, new Nest(registry, 123, 1), 1); expect(test.type).toEqual('B'); expect((test.value as Enum).type).toEqual('D'); @@ -293,13 +304,13 @@ describe('Enum', (): void => { describe('toRawType', (): void => { it('has a sane output for basic enums', (): void => { expect( - new Enum(['foo', 'bar']).toRawType() + new Enum(registry, ['foo', 'bar']).toRawType() ).toEqual(JSON.stringify({ _enum: ['foo', 'bar'] })); }); it('has a sane output for typed enums', (): void => { expect( - new Enum({ foo: Text, bar: U32 }).toRawType() + new Enum(registry, { foo: Text, bar: U32 }).toRawType() ).toEqual(JSON.stringify({ _enum: { foo: 'Text', bar: 'u32' } })); }); }); @@ -310,7 +321,7 @@ describe('Enum', (): void => { A: Text, B: U32 }); - const test = new Test(123, 1); + const test = new Test(registry, 123, 1); expect(test.toHex()).toEqual('0x017b000000'); expect(test.encodedLength).toEqual(1 + 4); diff --git a/packages/types/src/codec/Enum.ts b/packages/types/src/codec/Enum.ts index 01f756c302a8..3105e37409ee 100644 --- a/packages/types/src/codec/Enum.ts +++ b/packages/types/src/codec/Enum.ts @@ -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 { AnyJson, Codec, Constructor, InterfaceTypes } from '../types'; +import { AnyJson, Codec, Constructor, InterfaceTypes, Registry } from '../types'; import { assert, hexToU8a, isHex, isNumber, isObject, isString, isU8a, isUndefined, stringCamelCase, stringUpperFirst, u8aConcat, u8aToHex } from '@polkadot/util'; @@ -13,7 +13,7 @@ import Struct from './Struct'; // export interface, this is used in Enum.with, so required as public by TS export interface EnumConstructor { - new(value?: any, index?: number): T; + new(registry: Registry, value?: any, index?: number): T; } type TypesDef = Record; @@ -23,9 +23,9 @@ interface Decoded { value: Codec; } -function extractDef (_def: Record | string[]): { def: TypesDef; isBasic: boolean } { +function extractDef (registry: Registry, _def: Record | string[]): { def: TypesDef; isBasic: boolean } { if (!Array.isArray(_def)) { - const def = mapToTypeMap(_def); + const def = mapToTypeMap(registry, _def); const isBasic = !Object.values(def).some((type): boolean => type !== Null); return { @@ -44,18 +44,18 @@ function extractDef (_def: Record | string }; } -function createFromValue (def: TypesDef, index = 0, value?: any): Decoded { +function createFromValue (registry: Registry, def: TypesDef, index = 0, value?: any): Decoded { const Clazz = Object.values(def)[index]; assert(!isUndefined(Clazz), `Unable to create Enum via index ${index}, in ${Object.keys(def).join(', ')}`); return { index, - value: new Clazz(value) + value: new Clazz(registry, value) }; } -function decodeFromJSON (def: TypesDef, key: string, value?: any): Decoded { +function decodeFromJSON (registry: Registry, def: TypesDef, key: string, value?: any): Decoded { // JSON comes in the form of { "": "" }, here we // additionally force to lower to ensure forward compat const keys = Object.keys(def).map((k): string => k.toLowerCase()); @@ -64,31 +64,31 @@ function decodeFromJSON (def: TypesDef, key: string, value?: any): Decoded { assert(index !== -1, `Cannot map Enum JSON, unable to find '${key}' in ${keys.join(', ')}`); - return createFromValue(def, index, value); + return createFromValue(registry, def, index, value); } -function decodeFromString (def: TypesDef, value: string): Decoded { +function decodeFromString (registry: Registry, def: TypesDef, value: string): Decoded { return isHex(value) // eslint-disable-next-line @typescript-eslint/no-use-before-define - ? decodeFromValue(def, hexToU8a(value)) - : decodeFromJSON(def, value); + ? decodeFromValue(registry, def, hexToU8a(value)) + : decodeFromJSON(registry, def, value); } -function decodeFromValue (def: TypesDef, value?: any): Decoded { +function decodeFromValue (registry: Registry, def: TypesDef, value?: any): Decoded { if (isU8a(value)) { - return createFromValue(def, value[0], value.subarray(1)); + return createFromValue(registry, def, value[0], value.subarray(1)); } else if (isNumber(value)) { - return createFromValue(def, value); + return createFromValue(registry, def, value); } else if (isString(value)) { - return decodeFromString(def, value.toString()); + return decodeFromString(registry, def, value.toString()); } else if (isObject(value)) { const key = Object.keys(value)[0]; - return decodeFromJSON(def, key, value[key]); + return decodeFromJSON(registry, def, key, value[key]); } // Worst-case scenario, return the first with default - return createFromValue(def, 0); + return createFromValue(registry, def, 0); } /** @@ -109,11 +109,11 @@ export default class Enum extends Base { private _isBasic: boolean; - constructor (def: Record | string[], value?: any, index?: number) { - const defInfo = extractDef(def); - const decoded = Enum.decodeEnum(defInfo.def, value, index); + constructor (registry: Registry, def: Record | string[], value?: any, index?: number) { + const defInfo = extractDef(registry, def); + const decoded = Enum.decodeEnum(registry, defInfo.def, value, index); - super(decoded.value); + super(registry, decoded.value); this._def = defInfo.def; this._isBasic = defInfo.isBasic; @@ -121,23 +121,23 @@ export default class Enum extends Base { this._index = this._indexes.indexOf(decoded.index) || 0; } - private static decodeEnum (def: TypesDef, value?: any, index?: number): Decoded { + private static decodeEnum (registry: Registry, def: TypesDef, value?: any, index?: number): Decoded { // NOTE We check the index path first, before looking at values - this allows treating // the optional indexes before anything else, more-specific > less-specific if (isNumber(index)) { - return createFromValue(def, index, value); + return createFromValue(registry, def, index, value); } else if (value instanceof Enum) { - return createFromValue(def, value._index, value.raw); + return createFromValue(registry, def, value._index, value.raw); } // Or else, we just look at `value` - return decodeFromValue(def, value); + return decodeFromValue(registry, def, value); } public static with (Types: Record | string[]): EnumConstructor { return class extends Enum { - constructor (value?: any, index?: number) { - super(Types, value, index); + constructor (registry: Registry, value?: any, index?: number) { + super(registry, Types, value, index); Object.keys(this._def).forEach((_key): void => { const name = stringUpperFirst(stringCamelCase(_key.replace(' ', '_'))); @@ -261,7 +261,7 @@ export default class Enum extends Base { protected toRawStruct (): string[] | Record { return this._isBasic ? Object.keys(this._def) - : Struct.typesToMap(this._def); + : Struct.typesToMap(this.registry, this._def); } /** diff --git a/packages/types/src/codec/Int.spec.ts b/packages/types/src/codec/Int.spec.ts index ba2de054e910..faf723b04917 100644 --- a/packages/types/src/codec/Int.spec.ts +++ b/packages/types/src/codec/Int.spec.ts @@ -2,50 +2,53 @@ // 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 Int from './Int'; describe('Int', (): void => { + const registry = new TypeRegistry(); + it('provides a toBn interface', (): void => { expect( - new Int(-1234).toBn().toNumber() + new Int(registry, -1234).toBn().toNumber() ).toEqual(-1234); }); it('provides a toNumber interface', (): void => { expect( - new Int(-1234).toNumber() + new Int(registry, -1234).toNumber() ).toEqual(-1234); }); it('converts to Little Endian from the provided value', (): void => { expect( - new Int(-1234).toU8a() + new Int(registry, -1234).toU8a() ).toEqual(new Uint8Array([46, 251, 255, 255, 255, 255, 255, 255])); }); it('converts to Little Endian from the provided value (bitLength)', (): void => { expect( - new Int(-1234, 32).toU8a() + new Int(registry, -1234, 32).toU8a() ).toEqual(new Uint8Array([46, 251, 255, 255])); }); it('converts to hex/string', (): void => { - const i = new Int('0x12', 16); + const i = new Int(registry, '0x12', 16); expect(i.toHex()).toEqual('0x0012'); expect(i.toString()).toEqual('18'); }); it('converts to equivalents', (): void => { - const a = new Int('-123'); + const a = new Int(registry, '-123'); expect( - new Int(a).toNumber() + new Int(registry, a).toNumber() ).toEqual(-123); }); it('converts to JSON depending on flags', (): void => { - expect(new Int(0x12, 16).toJSON()).toEqual('0x0012'); - expect(new Int(0x12, 16, false).toJSON()).toEqual(0x12); + expect(new Int(registry, 0x12, 16).toJSON()).toEqual('0x0012'); + expect(new Int(registry, 0x12, 16, false).toJSON()).toEqual(0x12); }); }); diff --git a/packages/types/src/codec/Int.ts b/packages/types/src/codec/Int.ts index 9e447a8e707d..5b95f190b61c 100644 --- a/packages/types/src/codec/Int.ts +++ b/packages/types/src/codec/Int.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import { bnToHex, bnToU8a } from '@polkadot/util'; @@ -19,15 +19,8 @@ import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt'; * @noInheritDoc */ export default class Int extends AbstractInt { - constructor ( - value: AnyNumber = 0, - bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = true) { - super( - true, - value, - bitLength, - isHexJson - ); + constructor (registry: Registry, value: AnyNumber = 0, bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = true) { + super(registry, true, value, bitLength, isHexJson); } /** diff --git a/packages/types/src/codec/Linkage.ts b/packages/types/src/codec/Linkage.ts index 737a744eae8a..71ac70e135e1 100644 --- a/packages/types/src/codec/Linkage.ts +++ b/packages/types/src/codec/Linkage.ts @@ -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 { Constructor, Codec, InterfaceTypes } from '../types'; +import { Constructor, Codec, InterfaceTypes, Registry } from '../types'; import Option from './Option'; import Struct from './Struct'; @@ -18,8 +18,8 @@ const EMPTY = new Uint8Array(); * @description The wrapper for the result from a LinkedMap */ export default class Linkage extends Struct { - constructor (Type: Constructor | InterfaceTypes, value?: any) { - super({ + constructor (registry: Registry, Type: Constructor | InterfaceTypes, value?: any) { + super(registry, { previous: Option.with(Type), next: Option.with(Type) }, value); @@ -27,8 +27,8 @@ export default class Linkage extends Struct { public static withKey (Type: Constructor | InterfaceTypes): Constructor> { return class extends Linkage { - constructor (value?: any) { - super(Type, value); + constructor (registry: Registry, value?: any) { + super(registry, Type, value); } }; } @@ -65,8 +65,8 @@ export default class Linkage extends Struct { * @description A Linkage keys/Values tuple */ export class LinkageResult extends Tuple { - constructor ([TypeKey, keys]: TypeWithValues, [TypeValue, values]: TypeWithValues) { - super({ + constructor (registry: Registry, [TypeKey, keys]: TypeWithValues, [TypeValue, values]: TypeWithValues) { + super(registry, { Keys: Vec.with(TypeKey), Values: Vec.with(TypeValue) }, [keys, values]); diff --git a/packages/types/src/codec/Option.spec.ts b/packages/types/src/codec/Option.spec.ts index 4734251a0e4f..d0442ed71c82 100644 --- a/packages/types/src/codec/Option.spec.ts +++ b/packages/types/src/codec/Option.spec.ts @@ -2,16 +2,17 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../injector'; - -import Option from './Option'; +import { TypeRegistry } from './create'; import Bytes from '../primitive/Bytes'; import U32 from '../primitive/U32'; import Text from '../primitive/Text'; +import Option from './Option'; + +const registry = new TypeRegistry(); const testDecode = (type: string, input: any, expected: any): void => it(`can decode from ${type}`, (): void => { - const o = new Option(Text, input); + const o = new Option(registry, Text, input); expect(o.toString()).toBe(expected); expect(o.isNone).toBe(!expected.length); @@ -19,27 +20,27 @@ const testDecode = (type: string, input: any, expected: any): void => const testEncode = (to: string, expected: any): void => it(`can encode ${to}`, (): void => { - const e = new Option(Text, 'foo'); + const e = new Option(registry, Text, 'foo'); expect((e as any)[to]()).toEqual(expected); }); describe('Option', (): void => { it('converts undefined/null to empty', (): void => { - expect(new Option(Text, undefined).isNone).toBe(true); - expect(new Option(Text, null).isNone).toBe(true); - expect(new Option(Text, 'test').isNone).toBe(false); + expect(new Option(registry, Text, undefined).isNone).toBe(true); + expect(new Option(registry, Text, null).isNone).toBe(true); + expect(new Option(registry, Text, 'test').isNone).toBe(false); }); it('converts an option to an option', (): void => { expect( - new Option(Text, new Option(Text, 'hello')).toString() + new Option(registry, Text, new Option(registry, Text, 'hello')).toString() ).toEqual('hello'); }); it('converts an option to an option (strings)', (): void => { expect( - new Option('Text', new Option('Text', 'hello')).toString() + new Option(registry, 'Text', new Option(registry, 'Text', 'hello')).toString() ).toEqual('hello'); }); @@ -50,7 +51,7 @@ describe('Option', (): void => { // whatc the hex prefix and length expect( - new Option(Bytes, HEX).toHex().substr(6) + new Option(registry, Bytes, HEX).toHex().substr(6) ).toEqual(HEX.substr(2)); }); @@ -58,7 +59,7 @@ describe('Option', (): void => { const HEX = '0x12345678'; expect( - (new Option(U32, HEX).unwrap() as U32).toNumber() + (new Option(registry, U32, HEX).unwrap() as U32).toNumber() ).toEqual(0x12345678); }); @@ -73,33 +74,33 @@ describe('Option', (): void => { it('has empty toString() (undefined)', (): void => { expect( - new Option(Text).toString() + new Option(registry, Text).toString() ).toEqual(''); }); it('has value toString() (provided)', (): void => { expect( - new Option(Text, new Uint8Array([1, 4 << 2, 49, 50, 51, 52])).toString() + new Option(registry, Text, new Uint8Array([1, 4 << 2, 49, 50, 51, 52])).toString() ).toEqual('1234'); }); it('converts toU8a() with', (): void => { expect( - new Option(Text, '1234').toU8a() + new Option(registry, Text, '1234').toU8a() ).toEqual(new Uint8Array([1, 4 << 2, 49, 50, 51, 52])); }); it('converts toU8a() without', (): void => { expect( - new Option(Text).toU8a() + new Option(registry, Text).toU8a() ).toEqual(new Uint8Array([0])); }); describe('utils', (): void => { - const testeq = new Option(Text, '1234'); + const testeq = new Option(registry, Text, '1234'); it('compares against other option', (): void => { - expect(testeq.eq(new Option(Text, '1234'))).toBe(true); + expect(testeq.eq(new Option(registry, Text, '1234'))).toBe(true); }); it('compares against raw value', (): void => { @@ -107,11 +108,11 @@ describe('Option', (): void => { }); it('unwraps to default if empty', (): void => { - expect(new Option(Text).unwrapOr('6789')).toBe('6789'); + expect(new Option(registry, Text).unwrapOr('6789')).toBe('6789'); }); it('unwraps to value if non-empty', (): void => { - expect((new Option(Text, '1234').unwrapOr(null) as Text).toString()).toBe('1234'); + expect((new Option(registry, Text, '1234').unwrapOr(null) as Text).toString()).toBe('1234'); }); }); }); diff --git a/packages/types/src/codec/Option.ts b/packages/types/src/codec/Option.ts index b970749c47d3..383f6113aa13 100644 --- a/packages/types/src/codec/Option.ts +++ b/packages/types/src/codec/Option.ts @@ -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 { Codec, Constructor, InterfaceTypes } from '../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../types'; import { isNull, isU8a, isUndefined, u8aToHex } from '@polkadot/util'; @@ -21,41 +21,41 @@ import Base from './Base'; export default class Option extends Base { private _Type: Constructor; - constructor (Type: Constructor | InterfaceTypes, value?: any) { - const Clazz = typeToConstructor(Type); + constructor (registry: Registry, Type: Constructor | InterfaceTypes, value?: any) { + const Clazz = typeToConstructor(registry, Type); - super(Option.decodeOption(Clazz, value)); + super(registry, Option.decodeOption(registry, Clazz, value)); this._Type = Clazz; } - public static decodeOption (Type: Constructor, value?: any): Codec { + public static decodeOption (registry: Registry, Type: Constructor, value?: any): Codec { if (isNull(value) || isUndefined(value) || value instanceof Null) { - return new Null(); + return new Null(registry); } else if (value instanceof Option) { - return Option.decodeOption(Type, value.value); + return Option.decodeOption(registry, Type, value.value); } else if (value instanceof Type) { // don't re-create, use as it (which also caters for derived types) return value; } else if (isU8a(value)) { // the isU8a check happens last in the if-tree - since the wrapped value // may be an instance of it, so Type and Option checks go in first - return Option.decodeOptionU8a(Type, value); + return Option.decodeOptionU8a(registry, Type, value); } - return new Type(value); + return new Type(registry, value); } - private static decodeOptionU8a (Type: Constructor, value: Uint8Array): Codec { + private static decodeOptionU8a (registry: Registry, Type: Constructor, value: Uint8Array): Codec { return !value.length || value[0] === 0 - ? new Null() - : new Type(value.subarray(1)); + ? new Null(registry) + : new Type(registry, value.subarray(1)); } public static with (Type: Constructor | InterfaceTypes): Constructor> { return class extends Option { - constructor (value?: any) { - super(Type, value); + constructor (registry: Registry, value?: any) { + super(registry, Type, value); } }; } @@ -122,7 +122,7 @@ export default class Option extends Base { * @description Returns the base runtime type name for this instance */ public toRawType (isBare?: boolean): string { - const wrapped = new this._Type().toRawType(); + const wrapped = new this._Type(this.registry).toRawType(); return isBare ? wrapped diff --git a/packages/types/src/codec/Result.spec.ts b/packages/types/src/codec/Result.spec.ts index bddaed73e3a3..dc2331bc9fd9 100644 --- a/packages/types/src/codec/Result.spec.ts +++ b/packages/types/src/codec/Result.spec.ts @@ -2,23 +2,22 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../injector'; - import { hexToString } from '@polkadot/util'; import { u32, Text } from '../primitive'; -import { createType } from './create'; +import { createType, TypeRegistry } from './create'; import Result from './Result'; describe('Result', (): void => { + const registry = new TypeRegistry(); const Type = Result.with({ Ok: u32, Error: Text }); it('has a sane toRawType representation', (): void => { - expect(new Type().toRawType()).toEqual('Result'); + expect(new Type(registry).toRawType()).toEqual('Result'); }); it('decodes from a u8a (success)', (): void => { - const result = new Type(new Uint8Array([0, 1, 2, 3, 4])); + const result = new Type(registry, new Uint8Array([0, 1, 2, 3, 4])); expect(result.isOk); expect(result.asOk.toU8a()).toEqual(new Uint8Array([1, 2, 3, 4])); @@ -29,7 +28,7 @@ describe('Result', (): void => { }); it('decodes from a u8a (error)', (): void => { - const result = new Type(new Uint8Array([1, 4 << 2, 100, 101, 102, 103])); + const result = new Type(registry, new Uint8Array([1, 4 << 2, 100, 101, 102, 103])); expect(result.isError); expect(result.asError.toU8a()).toEqual(new Uint8Array([4 << 2, 100, 101, 102, 103])); @@ -40,12 +39,12 @@ describe('Result', (): void => { }); it('decodes from a JSON representation', (): void => { - const result = new Type({ Error: 'error' }); + const result = new Type(registry, { Error: 'error' }); expect(result.toHex()).toEqual('0x01146572726f72'); }); it('returns a proper raw typedef rom a built-in', (): void => { - expect(createType('DispatchResult').toRawType()).toEqual('Result'); + expect(createType(registry, 'DispatchResult').toRawType()).toEqual('Result'); }); }); diff --git a/packages/types/src/codec/Result.ts b/packages/types/src/codec/Result.ts index 142c1c962b84..2774f7967a77 100644 --- a/packages/types/src/codec/Result.ts +++ b/packages/types/src/codec/Result.ts @@ -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 { Codec, Constructor, InterfaceTypes } from '../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../types'; import { assert } from '@polkadot/util'; @@ -14,15 +14,15 @@ import Enum from './Enum'; * A Result maps to the Rust Result type, that can either wrap a success or error value */ export default class Result extends Enum { - constructor (Ok: Constructor | InterfaceTypes, Error: Constructor | InterfaceTypes, value?: any) { + constructor (registry: Registry, Ok: Constructor | InterfaceTypes, Error: Constructor | InterfaceTypes, value?: any) { // NOTE This is order-dependent, Ok (with index 0) needs to be first - super({ Ok, Error }, value); + super(registry, { Ok, Error }, value); } public static with (Types: { Ok: Constructor | InterfaceTypes; Error: Constructor | InterfaceTypes }): Constructor> { return class extends Result { - constructor (value?: any) { - super(Types.Ok, Types.Error, value); + constructor (registry: Registry, value?: any) { + super(registry, Types.Ok, Types.Error, value); } }; } diff --git a/packages/types/src/codec/Set.spec.ts b/packages/types/src/codec/Set.spec.ts index 8e6841e01e19..a167c6b3e45f 100644 --- a/packages/types/src/codec/Set.spec.ts +++ b/packages/types/src/codec/Set.spec.ts @@ -2,6 +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 { TypeRegistry } from './create'; import Set from './Set'; const SET_FIELDS = { @@ -25,8 +26,10 @@ const SET_WITHDRAW = { }; describe('Set', (): void => { + const registry = new TypeRegistry(); + it('constructs via an string[]', (): void => { - const set = new Set(SET_ROLES, ['full', 'authority']); + const set = new Set(registry, SET_ROLES, ['full', 'authority']); expect(set.isEmpty).toEqual(false); expect(set.toString()).toEqual( @@ -36,36 +39,36 @@ describe('Set', (): void => { it('throws with invalid values', (): void => { expect( - (): Set => new Set(SET_ROLES, ['full', 'authority', 'invalid']) + (): Set => new Set(registry, SET_ROLES, ['full', 'authority', 'invalid']) ).toThrow(/Invalid key 'invalid'/); }); it('throws with add on invalid', (): void => { expect( - (): Set => (new Set(SET_ROLES, [])).add('invalid') + (): Set => (new Set(registry, SET_ROLES, [])).add('invalid') ).toThrow(/Invalid key 'invalid'/); }); it('allows construction via number', (): void => { expect( - (new Set(SET_WITHDRAW, 15)).eq(['TransactionPayment', 'Transfer', 'Reserve', 'Fee']) + (new Set(registry, SET_WITHDRAW, 15)).eq(['TransactionPayment', 'Transfer', 'Reserve', 'Fee']) ).toBe(true); }); it('does not allow invalid number', (): void => { expect( - (): Set => new Set(SET_WITHDRAW, 31) + (): Set => new Set(registry, SET_WITHDRAW, 31) ).toThrow(/Mismatch decoding '31', computed as '15'/); }); it('hash a valid encoding', (): void => { - const set = new Set(SET_FIELDS, ['header', 'body', 'justification']); + const set = new Set(registry, SET_FIELDS, ['header', 'body', 'justification']); expect(set.toU8a()).toEqual(new Uint8Array([19])); }); describe('utils', (): void => { - const set = new Set(SET_ROLES, ['full', 'authority']); + const set = new Set(registry, SET_ROLES, ['full', 'authority']); it('compares against string array', (): void => { expect( @@ -81,7 +84,7 @@ describe('Set', (): void => { it('compares against other sets', (): void => { expect( - set.eq(new Set(SET_ROLES, ['authority', 'full'])) + set.eq(new Set(registry, SET_ROLES, ['authority', 'full'])) ).toBe(true); }); @@ -94,7 +97,7 @@ describe('Set', (): void => { it('has a sane toRawType representation', (): void => { expect( - new Set({ a: 1, b: 2, c: 345 }).toRawType() + new Set(registry, { a: 1, b: 2, c: 345 }).toRawType() ).toEqual(JSON.stringify({ _set: { a: 1, b: 2, c: 345 } })); diff --git a/packages/types/src/codec/Set.ts b/packages/types/src/codec/Set.ts index 4c31ddcef62f..beee9f136893 100644 --- a/packages/types/src/codec/Set.ts +++ b/packages/types/src/codec/Set.ts @@ -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 { Codec, Constructor, IHash } from '../types'; +import { Codec, Constructor, IHash, Registry } from '../types'; import { assert, isU8a, isNumber, isUndefined, stringCamelCase, stringUpperFirst, u8aToHex } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -20,11 +20,14 @@ type SetValues = Record; */ // FIXME This is a prime candidate to extend the JavaScript built-in Set export default class CodecSet extends Set implements Codec { + public readonly registry: Registry; + private _setValues: SetValues; - constructor (setValues: SetValues, value?: string[] | Set | Uint8Array | number) { + constructor (registry: Registry, setValues: SetValues, value?: string[] | Set | Uint8Array | number) { super(CodecSet.decodeSet(setValues, value)); + this.registry = registry; this._setValues = setValues; } @@ -76,8 +79,8 @@ export default class CodecSet extends Set implements Codec { public static with (values: SetValues): Constructor { return class extends CodecSet { - constructor (value?: any) { - super(values, value); + constructor (registry: Registry, value?: any) { + super(registry, values, value); Object.keys(values).forEach((_key): void => { const name = stringUpperFirst(stringCamelCase(_key)); @@ -106,7 +109,7 @@ export default class CodecSet extends Set implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/Struct.spec.ts b/packages/types/src/codec/Struct.spec.ts index 0a38efdae519..2fa6cb106593 100644 --- a/packages/types/src/codec/Struct.spec.ts +++ b/packages/types/src/codec/Struct.spec.ts @@ -2,9 +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 '../injector'; - -import { ClassOf } from './create'; +import { ClassOf, TypeRegistry } from './create'; import AccountId from '../primitive/Generic/AccountId'; import Text from '../primitive/Text'; import U32 from '../primitive/U32'; @@ -13,10 +11,12 @@ import Struct from './Struct'; import Vec from './Vec'; describe('Struct', (): void => { + const registry = new TypeRegistry(); + describe('decoding', (): void => { const testDecode = (type: string, input: any): void => it(`can decode from ${type}`, (): void => { - const s = new Struct({ + const s = new Struct(registry, { foo: Text, bar: U32 }, input); @@ -37,7 +37,7 @@ describe('Struct', (): void => { describe('encoding', (): void => { const testEncode = (to: CodecTo, expected: any): void => it(`can encode ${to}`, (): void => { - const s = new Struct({ + const s = new Struct(registry, { foo: Text, bar: U32 }, { foo: 'bazzing', bar: 69 }); @@ -57,12 +57,12 @@ describe('Struct', (): void => { txt: Text, u32: U32 }) - )(null).toString() + )(registry, null).toString() ).toEqual('{}'); }); it('decodes a more complicated type', (): void => { - const s = new Struct({ + const s = new Struct(registry, { foo: Vec.with(Struct.with({ bar: Text })) @@ -71,11 +71,11 @@ describe('Struct', (): void => { }); it('decodes from a Map input', (): void => { - const input = new Struct({ + const input = new Struct(registry, { a: U32, txt: Text }, { a: 42, txt: 'fubar' }); - const s = new Struct({ + const s = new Struct(registry, { txt: Text, foo: U32, bar: U32 @@ -90,7 +90,7 @@ describe('Struct', (): void => { txt: Text, u32: U32 }) - )('ABC') + )(registry, 'ABC') ).toThrowError(/Struct: cannot decode type/); }); @@ -101,7 +101,7 @@ describe('Struct', (): void => { txt: Text, u32: U32 }) - )({ txt: 'foo', u32: 0x123456 }).toString() + )(registry, { txt: 'foo', u32: 0x123456 }).toString() ).toEqual('{"txt":"foo","u32":1193046}'); }); @@ -113,7 +113,7 @@ describe('Struct', (): void => { num: 'u32', cls: U32 }) - )({ txt: 'foo', num: 0x123456, cls: 123 }).toString() + )(registry, { txt: 'foo', num: 0x123456, cls: 123 }).toString() ).toEqual('{"txt":"foo","num":1193046,"cls":123}'); }); @@ -123,7 +123,7 @@ describe('Struct', (): void => { txt: Text, u32: U32 }) - )({ txt: 'foo', u32: 0x123456 }); + )(registry, { txt: 'foo', u32: 0x123456 }); expect((struct as any).txt.toString()).toEqual('foo'); expect((struct as any).u32.toNumber()).toEqual(0x123456); @@ -136,13 +136,13 @@ describe('Struct', (): void => { txt: Text, u32: U32 }) - )({ foo: 'bazzing', bar: 69 }).encodedLength + )(registry, { foo: 'bazzing', bar: 69 }).encodedLength ).toEqual(5); }); it('exposes the types', (): void => { expect( - new Struct({ + new Struct(registry, { foo: Text, bar: Text, baz: U32 @@ -165,7 +165,7 @@ describe('Struct', (): void => { txt: Text, u32: U32 }) - )({ txt: 'foo', u32: 1234 }) + )(registry, { txt: 'foo', u32: 1234 }) .getAtIndex(1) .toString() ).toEqual('1234'); @@ -180,7 +180,7 @@ describe('Struct', (): void => { }; expect( - new Struct({ + new Struct(registry, { foo: Text, bar: Text, baz: U32 @@ -192,20 +192,20 @@ describe('Struct', (): void => { it('allows toString with large numbers', (): void => { // replicate https://github.com/polkadot-js/api/issues/640 expect( - new Struct({ - blockNumber: ClassOf('Option') + new Struct(registry, { + blockNumber: ClassOf(registry, 'Option') }, { blockNumber: '0x0000000010abcdef' }).toString() ).toEqual('{"blockNumber":279694831}'); }); it('generates sane toRawType', (): void => { expect( - new Struct({ + new Struct(registry, { accountId: AccountId, - balanceCompact: ClassOf('Compact'), - blockNumber: ClassOf('BlockNumber'), - compactNumber: ClassOf('Compact'), - optionNumber: ClassOf('Option'), + balanceCompact: ClassOf(registry, 'Compact'), + blockNumber: ClassOf(registry, 'BlockNumber'), + compactNumber: ClassOf(registry, 'Compact'), + optionNumber: ClassOf(registry, 'Option'), counter: U32, vector: Vec.with(AccountId) }).toRawType() @@ -223,11 +223,11 @@ describe('Struct', (): void => { it('generates sane toRawType (via with)', (): void => { const Type = Struct.with({ accountId: AccountId, - balance: ClassOf('Balance') + balance: ClassOf(registry, 'Balance') }); expect( - new Type().toRawType() + new Type(registry).toRawType() ).toEqual(JSON.stringify({ accountId: 'AccountId', balance: 'Balance' // Override in Uint diff --git a/packages/types/src/codec/Struct.ts b/packages/types/src/codec/Struct.ts index fe31da12989b..453b57c0255f 100644 --- a/packages/types/src/codec/Struct.ts +++ b/packages/types/src/codec/Struct.ts @@ -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 { AnyJsonObject, Codec, Constructor, ConstructorDef, IHash, InterfaceTypes } from '../types'; +import { AnyJsonObject, Codec, Constructor, ConstructorDef, IHash, InterfaceTypes, Registry } from '../types'; import { hexToU8a, isHex, isObject, isU8a, isUndefined, u8aConcat, u8aToHex } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -31,16 +31,19 @@ export default class Struct< 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 implements Codec { + public readonly registry: Registry; + protected _jsonMap: Map; protected _Types: ConstructorDef; - constructor (Types: S, value: V | Map | any[] | string = {} as unknown as V, jsonMap: Map = new Map()) { - const Clazzes = mapToTypeMap(Types); - const decoded: T = Struct.decodeStruct(Clazzes, value, jsonMap); + constructor (registry: Registry, Types: S, value: V | Map | any[] | string = {} as unknown as V, jsonMap: Map = new Map()) { + const Clazzes = mapToTypeMap(registry, Types); + const decoded: T = Struct.decodeStruct(registry, Clazzes, value, jsonMap); super(Object.entries(decoded)); + this.registry = registry; this._jsonMap = jsonMap; this._Types = Clazzes; } @@ -60,11 +63,11 @@ export default class Struct< * `Object.keys(Types)` * @param jsonMap */ - private static decodeStruct (Types: ConstructorDef, value: any, jsonMap: Map): T { + private static decodeStruct (registry: Registry, Types: ConstructorDef, value: any, jsonMap: Map): T { if (isHex(value)) { - return Struct.decodeStruct(Types, hexToU8a(value as string), jsonMap); + return Struct.decodeStruct(registry, Types, hexToU8a(value as string), jsonMap); } else if (isU8a(value)) { - const values = decodeU8a(value, Object.values(Types)); + const values = decodeU8a(registry, value, Object.values(Types)); // Transform array of values to {key: value} mapping return Object.keys(Types).reduce((raw, key, index): T => { @@ -78,10 +81,10 @@ export default class Struct< } // We assume from here that value is a JS object (Array, Map, Object) - return Struct.decodeStructFromObject(Types, value, jsonMap); + return Struct.decodeStructFromObject(registry, Types, value, jsonMap); } - private static decodeStructFromObject (Types: ConstructorDef, value: any, jsonMap: Map): T { + private static decodeStructFromObject (registry: Registry, Types: ConstructorDef, value: any, jsonMap: Map): T { return Object.keys(Types).reduce((raw, key, index): T => { // The key in the JSON can be snake_case (or other cases), but in our // Types, result or any other maps, it's camelCase @@ -92,17 +95,17 @@ export default class Struct< // TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'. (raw as any)[key] = value[index] instanceof Types[key] ? value[index] - : new Types[key](value[index]); + : new Types[key](registry, value[index]); } else if (value instanceof Map) { const mapped = value.get(jsonKey); (raw as any)[key] = mapped instanceof Types[key] ? mapped - : new Types[key](mapped); + : new Types[key](registry, mapped); } else if (isObject(value)) { (raw as any)[key] = value[jsonKey as string] instanceof Types[key] ? value[jsonKey as string] - : new Types[key](value[jsonKey as string]); + : new Types[key](registry, value[jsonKey as string]); } else { throw new Error(`Struct: cannot decode type ${Types[key].name} with value ${JSON.stringify(value)}`); } @@ -116,8 +119,8 @@ export default class Struct< public static with (Types: S): Constructor> { return class extends Struct { - constructor (value?: any, jsonMap?: Map) { - super(Types, value, jsonMap); + constructor (registry: Registry, value?: any, jsonMap?: Map) { + super(registry, Types, value, jsonMap); (Object.keys(Types) as (keyof S)[]).forEach((key): void => { // do not clobber existing properties on the object @@ -156,7 +159,7 @@ export default class Struct< return (Object .entries(this._Types) as [keyof S, Constructor][]) .reduce((result: E, [key, Type]): E => { - (result as any)[key] = new Type().toRawType(); + (result as any)[key] = new Type(this.registry).toRawType(); return result; }, {} as unknown as E); @@ -177,7 +180,7 @@ export default class Struct< * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** @@ -232,9 +235,9 @@ export default class Struct< }, {} as any); } - public static typesToMap (Types: Record): Record { + public static typesToMap (registry: Registry, Types: Record): Record { return Object.entries(Types).reduce((result, [key, Type]): Record => { - result[key] = new Type().toRawType(); + result[key] = new Type(registry).toRawType(); return result; }, {} as unknown as Record); @@ -245,7 +248,7 @@ export default class Struct< */ public toRawType (): string { return JSON.stringify( - Struct.typesToMap(this._Types) + Struct.typesToMap(this.registry, this._Types) ); } diff --git a/packages/types/src/codec/StructAny.ts b/packages/types/src/codec/StructAny.ts index a9a95f9fe002..7a8bc8b07579 100644 --- a/packages/types/src/codec/StructAny.ts +++ b/packages/types/src/codec/StructAny.ts @@ -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 { AnyJsonObject, Codec, IHash } from '../types'; +import { AnyJsonObject, Codec, IHash, Registry } from '../types'; import { isUndefined } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -20,11 +20,15 @@ import { compareMap } from './utils'; * @noInheritDoc */ export default class StructAny extends Map implements Codec { - constructor (value?: { [index: string]: any } | null) { + public readonly registry: Registry; + + constructor (registry: Registry, value?: { [index: string]: any } | null) { const decoded = StructAny.decodeJson(value); super(decoded); + this.registry = registry; + // like we are doing with structs, add the keys as getters decoded.forEach(([key]): void => { // do not clobber existing properties on the object @@ -54,7 +58,7 @@ export default class StructAny extends Map implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/Tuple.spec.ts b/packages/types/src/codec/Tuple.spec.ts index bf7c7c535002..9942b3a3b156 100644 --- a/packages/types/src/codec/Tuple.spec.ts +++ b/packages/types/src/codec/Tuple.spec.ts @@ -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 '../injector'; - import { VoteThreshold } from '../interfaces/elections'; import { BlockNumber } from '../interfaces/runtime'; import { CodecTo } from '../types'; @@ -11,18 +9,19 @@ import { CodecTo } from '../types'; import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; -import { ClassOf } from './create'; -import Call from '../primitive/Generic/Call'; +import { ClassOf, TypeRegistry } from './create'; import Text from '../primitive/Text'; import U32 from '../primitive/U32'; import U128 from '../primitive/U128'; import Tuple from './Tuple'; describe('Tuple', (): void => { + const registry = new TypeRegistry(); let tuple: Tuple; beforeEach((): void => { tuple = new Tuple( + registry, [Text, U32], ['bazzing', 69] ); @@ -31,7 +30,7 @@ describe('Tuple', (): void => { describe('decoding', (): void => { const testDecode = (type: string, input: any): void => it(`can decode from ${type}`, (): void => { - const t = new Tuple([ + const t = new Tuple(registry, [ Text, U32 ], input); @@ -60,6 +59,7 @@ describe('Tuple', (): void => { it('creates from string types', (): void => { expect( new Tuple( + registry, ['Text', 'u32', U32], ['foo', 69, 42] ).toString() @@ -67,12 +67,13 @@ describe('Tuple', (): void => { }); it('creates properly via actual hex string', (): void => { - Call.injectMetadata(new Metadata(rpcMetadata)); + // eslint-disable-next-line no-new + new Metadata(registry, rpcMetadata); const test = new (Tuple.with([ - ClassOf('BlockNumber'), ClassOf('VoteThreshold') + ClassOf(registry, 'BlockNumber'), ClassOf(registry, 'VoteThreshold') ] - ))('0x6219000001'); + ))(registry, '0x6219000001'); expect((test[0] as BlockNumber).toNumber()).toEqual(6498); expect((test[1] as VoteThreshold).toNumber()).toEqual(1); @@ -83,16 +84,16 @@ describe('Tuple', (): void => { }); it('exposes the Types (object creation)', (): void => { - const test = new Tuple({ - BlockNumber: ClassOf('BlockNumber'), - VoteThreshold: ClassOf('VoteThreshold') + const test = new Tuple(registry, { + BlockNumber: ClassOf(registry, 'BlockNumber'), + VoteThreshold: ClassOf(registry, 'VoteThreshold') }, []); expect(test.Types).toEqual(['BlockNumber', 'VoteThreshold']); }); it('exposes filter', (): void => { - expect(tuple.filter((v): boolean => v.toJSON() === 69)).toEqual([new U32(69)]); + expect(tuple.filter((v): boolean => v.toJSON() === 69)).toEqual([new U32(registry, 69)]); }); it('exposes map', (): void => { @@ -112,13 +113,13 @@ describe('Tuple', (): void => { describe('toRawType', (): void => { it('generates sane value with array types', (): void => { expect( - new Tuple([U128, ClassOf('BlockNumber')]).toRawType() + new Tuple(registry, [U128, ClassOf(registry, 'BlockNumber')]).toRawType() ).toEqual('(u128,u32)'); }); it('generates sane value with object types', (): void => { expect( - new Tuple({ number: U128, blockNumber: ClassOf('BlockNumber') }).toRawType() + new Tuple(registry, { number: U128, blockNumber: ClassOf(registry, 'BlockNumber') }).toRawType() ).toEqual('(u128,u32)'); }); }); diff --git a/packages/types/src/codec/Tuple.ts b/packages/types/src/codec/Tuple.ts index 140b44425984..9a8a79f509ff 100644 --- a/packages/types/src/codec/Tuple.ts +++ b/packages/types/src/codec/Tuple.ts @@ -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 { AnyNumber, AnyU8a, AnyString, Codec, Constructor, InterfaceTypes } from '../types'; +import { AnyNumber, AnyU8a, AnyString, Codec, Constructor, InterfaceTypes, Registry } from '../types'; import { isU8a, u8aConcat, isHex, hexToU8a } from '@polkadot/util'; @@ -26,21 +26,21 @@ type TupleTypes = (Constructor | InterfaceTypes)[] | { export default class Tuple extends AbstractArray { private _Types: TupleConstructors; - constructor (Types: TupleTypes, value?: any) { + constructor (registry: Registry, Types: TupleTypes, value?: any) { const Clazzes = Array.isArray(Types) - ? Types.map((type): Constructor => typeToConstructor(type)) - : mapToTypeMap(Types); + ? Types.map((type): Constructor => typeToConstructor(registry, type)) + : mapToTypeMap(registry, Types); - super(...Tuple.decodeTuple(Clazzes, value)); + super(registry, ...Tuple.decodeTuple(registry, Clazzes, value)); this._Types = Clazzes; } - private static decodeTuple (_Types: TupleConstructors, value: AnyU8a | string | (AnyU8a | AnyNumber | AnyString | undefined | null)[]): Codec[] { + private static decodeTuple (registry: Registry, _Types: TupleConstructors, value: AnyU8a | string | (AnyU8a | AnyNumber | AnyString | undefined | null)[]): Codec[] { if (isU8a(value)) { - return decodeU8a(value, _Types); + return decodeU8a(registry, value, _Types); } else if (isHex(value)) { - return Tuple.decodeTuple(_Types, hexToU8a(value)); + return Tuple.decodeTuple(registry, _Types, hexToU8a(value)); } const Types: Constructor[] = Array.isArray(_Types) @@ -49,7 +49,7 @@ export default class Tuple extends AbstractArray { return Types.map((Type, index): Codec => { try { - return new Type(value && value[index]); + return new Type(registry, value && value[index]); } catch (error) { throw new Error(`Tuple: failed on ${index}:: ${error.message}`); } @@ -58,8 +58,8 @@ export default class Tuple extends AbstractArray { public static with (Types: TupleTypes): Constructor { return class extends Tuple { - constructor (value?: any) { - super(Types, value); + constructor (registry: Registry, value?: any) { + super(registry, Types, value); } }; } @@ -80,7 +80,7 @@ export default class Tuple extends AbstractArray { */ public get Types (): string[] { return Array.isArray(this._Types) - ? this._Types.map((Type): string => new Type().toRawType()) + ? this._Types.map((Type): string => new Type(this.registry).toRawType()) : Object.keys(this._Types); } @@ -92,7 +92,7 @@ export default class Tuple extends AbstractArray { Array.isArray(this._Types) ? this._Types : Object.values(this._Types) - ).map((Type): string => new Type().toRawType()); + ).map((Type): string => new Type(this.registry).toRawType()); return `(${types.join(',')})`; } diff --git a/packages/types/src/codec/U8a.spec.ts b/packages/types/src/codec/U8a.spec.ts index a1099fccf898..7b6007f73eb7 100644 --- a/packages/types/src/codec/U8a.spec.ts +++ b/packages/types/src/codec/U8a.spec.ts @@ -3,18 +3,22 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { CodecTo } from '../types'; + +import { TypeRegistry } from './create'; import U8a from './U8a'; +const registry = new TypeRegistry(); + const testDecode = (type: string, input: any, expected: string): void => it(`can decode from ${type}`, (): void => { - const e = new U8a(input); + const e = new U8a(registry, input); expect(e.toString()).toBe(expected); }); const testEncode = (to: CodecTo, expected: any): void => it(`can encode ${to}`, (): void => { - const e = new U8a([1, 2, 3, 4, 5]); + const e = new U8a(registry, [1, 2, 3, 4, 5]); expect(e[to]()).toEqual(expected); }); @@ -23,7 +27,7 @@ describe('U8a', (): void => { let u8a: U8a; beforeEach((): void => { - u8a = new U8a([1, 2, 3, 4, 5]); + u8a = new U8a(registry, [1, 2, 3, 4, 5]); }); testDecode('Array', [1, 2, 3, 4, 5], '0x0102030405'); @@ -46,7 +50,7 @@ describe('U8a', (): void => { it('allows wrapping of a pre-existing instance', (): void => { expect( - new U8a(u8a).length + new U8a(registry, u8a).length ).toEqual(5); }); diff --git a/packages/types/src/codec/U8a.ts b/packages/types/src/codec/U8a.ts index 36bb0e630757..495c60dc43ca 100644 --- a/packages/types/src/codec/U8a.ts +++ b/packages/types/src/codec/U8a.ts @@ -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 { AnyU8a, Codec, IHash } from '../types'; +import { AnyU8a, Codec, IHash, Registry } from '../types'; import { isU8a, isUndefined, u8aToHex, u8aToU8a } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -17,10 +17,12 @@ import { blake2AsU8a } from '@polkadot/util-crypto'; * @noInheritDoc */ export default class U8a extends Uint8Array implements Codec { - constructor (value?: AnyU8a) { - super( - U8a.decodeU8a(value) - ); + public readonly registry: Registry; + + constructor (registry: Registry, value?: AnyU8a) { + super(U8a.decodeU8a(value)); + + this.registry = registry; } private static decodeU8a (value?: any): Uint8Array { @@ -42,7 +44,7 @@ export default class U8a extends Uint8Array implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return new U8a(blake2AsU8a(this.toU8a(), 256)); + return new U8a(this.registry, blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/codec/U8aFixed.spec.ts b/packages/types/src/codec/U8aFixed.spec.ts index 0444f95cce42..95179b4bbf72 100644 --- a/packages/types/src/codec/U8aFixed.spec.ts +++ b/packages/types/src/codec/U8aFixed.spec.ts @@ -2,25 +2,28 @@ // 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 U8aFixed from './U8aFixed'; describe('U8aFixed', (): void => { + const registry = new TypeRegistry(); + describe('construction', (): void => { it('allows empty values', (): void => { expect( - new U8aFixed().toHex() + new U8aFixed(registry).toHex() ).toEqual('0x0000000000000000000000000000000000000000000000000000000000000000'); }); it('allows construction via with', (): void => { expect( - new (U8aFixed.with(64))().bitLength() + new (U8aFixed.with(64))(registry).bitLength() ).toEqual(64); }); it('constructs from hex', (): void => { expect( - new (U8aFixed.with(32))('0x01020304').toU8a() + new (U8aFixed.with(32))(registry, '0x01020304').toU8a() ).toEqual( new Uint8Array([0x01, 0x02, 0x03, 0x04]) ); @@ -28,7 +31,7 @@ describe('U8aFixed', (): void => { it('constructs from number[]', (): void => { expect( - new (U8aFixed.with(32))([0x02, 0x03]).toU8a() + new (U8aFixed.with(32))(registry, [0x02, 0x03]).toU8a() ).toEqual( new Uint8Array([0x02, 0x03, 0x00, 0x00]) ); @@ -39,7 +42,7 @@ describe('U8aFixed', (): void => { let u8a: U8aFixed; beforeEach((): void => { - u8a = new U8aFixed([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 32); + u8a = new U8aFixed(registry, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 32); }); it('limits the length', (): void => { diff --git a/packages/types/src/codec/U8aFixed.ts b/packages/types/src/codec/U8aFixed.ts index 75ec979a7a05..ae9defc72797 100644 --- a/packages/types/src/codec/U8aFixed.ts +++ b/packages/types/src/codec/U8aFixed.ts @@ -4,7 +4,7 @@ import { isString, u8aToU8a } from '@polkadot/util'; -import { AnyU8a, Constructor } from '../types'; +import { AnyU8a, Constructor, Registry } from '../types'; import U8a from './U8a'; @@ -20,10 +20,8 @@ export type BitLength = 8 | 16 | 32 | 64 | 128 | 160 | 256 | 512 | 520 | 1024 | * to be used directly, rather is should be subclassed with the specific lengths. */ export default class U8aFixed extends U8a { - constructor (value: AnyU8a = new Uint8Array(), bitLength: BitLength = 256) { - super( - U8aFixed.decodeU8aFixed(value, bitLength) - ); + constructor (registry: Registry, value: AnyU8a = new Uint8Array(), bitLength: BitLength = 256) { + super(registry, U8aFixed.decodeU8aFixed(value, bitLength)); } private static decodeU8aFixed (value: AnyU8a, bitLength: BitLength): AnyU8a { @@ -49,8 +47,8 @@ export default class U8aFixed extends U8a { public static with (bitLength: BitLength): Constructor { return class extends U8aFixed { - constructor (value?: any) { - super(value, bitLength); + constructor (registry: Registry, value?: any) { + super(registry, value, bitLength); } }; } diff --git a/packages/types/src/codec/UInt.spec.ts b/packages/types/src/codec/UInt.spec.ts index fc62b10f515b..91a362c02402 100644 --- a/packages/types/src/codec/UInt.spec.ts +++ b/packages/types/src/codec/UInt.spec.ts @@ -4,56 +4,59 @@ import BN from 'bn.js'; +import { TypeRegistry } from './create'; import UInt from './UInt'; describe('UInt', (): void => { + const registry = new TypeRegistry(); + it('provides a toBn interface', (): void => { expect( - new UInt(987).toBn().toNumber() + new UInt(registry, 987).toBn().toNumber() ).toEqual(987); }); it('provides a toNumber interface', (): void => { expect( - new UInt(4567).toNumber() + new UInt(registry, 4567).toNumber() ).toEqual(4567); }); it('converts to Little Endian from the provided value', (): void => { expect( - new UInt(1234567).toU8a() + new UInt(registry, 1234567).toU8a() ).toEqual(new Uint8Array([135, 214, 18, 0, 0, 0, 0, 0])); }); it('converts to Little Endian from the provided value (bitLength)', (): void => { expect( - new UInt(1234567, 32).toU8a() + new UInt(registry, 1234567, 32).toU8a() ).toEqual(new Uint8Array([135, 214, 18, 0])); }); it('converts to hex/string', (): void => { - const u = new UInt('0x12', 16); + const u = new UInt(registry, '0x12', 16); expect(u.toHex()).toEqual('0x0012'); expect(u.toString()).toEqual('18'); }); it('converts to equivalents', (): void => { - const a = new UInt('123'); + const a = new UInt(registry, '123'); expect( - new UInt(a).toNumber() + new UInt(registry, a).toNumber() ).toEqual(123); }); it('converts to JSON representation based on flags/size', (): void => { - expect(new UInt('0x12345678', 64, true).toJSON()).toEqual('0x0000000012345678'); - expect(new UInt('0x1234567890', 64, false).toJSON()).toEqual(0x1234567890); - expect(new UInt('0x1234567890abcdef', 64, false).toJSON()).toEqual('0x1234567890abcdef'); + expect(new UInt(registry, '0x12345678', 64, true).toJSON()).toEqual('0x0000000012345678'); + expect(new UInt(registry, '0x1234567890', 64, false).toJSON()).toEqual(0x1234567890); + expect(new UInt(registry, '0x1234567890abcdef', 64, false).toJSON()).toEqual('0x1234567890abcdef'); }); describe('eq', (): void => { - const test = new UInt(12345); + const test = new UInt(registry, 12345); it('compares against other BN values', (): void => { expect(test.eq(new BN(12345))).toBe(true); diff --git a/packages/types/src/codec/UInt.ts b/packages/types/src/codec/UInt.ts index 4495b62ad60f..51c6d2fec34b 100644 --- a/packages/types/src/codec/UInt.ts +++ b/packages/types/src/codec/UInt.ts @@ -2,11 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AnyNumber } from '../types'; +import { AnyNumber, Constructor, Registry } from '../types'; import { bnToHex, bnToU8a } from '@polkadot/util'; -import { ClassOf } from './create'; +import { ClassOf } from './create/createClass'; import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt'; /** @@ -20,15 +20,16 @@ import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt'; * @noInheritDoc */ export default class UInt extends AbstractInt { - constructor ( - value: AnyNumber = 0, - bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = false) { - super( - false, - value, - bitLength, - isHexJson - ); + constructor (registry: Registry, value: AnyNumber = 0, bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = false) { + super(registry, false, value, bitLength, isHexJson); + } + + public static with (bitLength?: UIntBitLength): Constructor { + return class extends UInt { + constructor (registry: Registry, value?: any) { + super(registry, value, bitLength); + } + }; } /** @@ -50,7 +51,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('Balance') + return this instanceof ClassOf(this.registry, 'Balance') ? 'Balance' : `u${this._bitLength}`; } diff --git a/packages/types/src/codec/Vec.spec.ts b/packages/types/src/codec/Vec.spec.ts index 471afc556c5c..30879b57726c 100644 --- a/packages/types/src/codec/Vec.spec.ts +++ b/packages/types/src/codec/Vec.spec.ts @@ -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 '../injector'; - import { PropIndex } from '../interfaces/democracy'; import { Codec, CodecTo } from '../types'; @@ -11,19 +9,21 @@ import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import AccountId from '../primitive/Generic/AccountId'; -import Call from '../primitive/Generic/Call'; import Text from '../primitive/Text'; -import { createTypeUnsafe } from './create'; +import { createTypeUnsafe, TypeRegistry } from './create'; import Vec from './Vec'; import Tuple from './Tuple'; +const registry = new TypeRegistry(); + +// eslint-disable-next-line no-new +new Metadata(registry, rpcMetadata); + describe('Vec', (): void => { let vector: Vec; beforeEach((): void => { - vector = new Vec(Text, ['1', '23', '345', '4567', new Text('56789')]); - - Call.injectMetadata(new Metadata(rpcMetadata)); + vector = new Vec(registry, Text, ['1', '23', '345', '4567', new Text(registry, '56789')]); }); it('wraps a sequence of values', (): void => { @@ -45,15 +45,15 @@ describe('Vec', (): void => { ])); }); - it('allows contruction via JSON', (): void => { + it('allows construction via JSON', (): void => { expect( - new Vec(Text, ['6', '7']).toJSON() + new Vec(registry, Text, ['6', '7']).toJSON() ).toEqual(['6', '7']); }); - it('allows contruction via JSON (string type)', (): void => { + it('allows construction via JSON (string type)', (): void => { expect( - new Vec('u32', ['6', '7']).toJSON() + new Vec(registry, 'u32', ['6', '7']).toJSON() ).toEqual([6, 7]); }); @@ -62,7 +62,7 @@ describe('Vec', (): void => { }); it('decodes a complex type via construction', (): void => { - const test = createTypeUnsafe('Vec<(PropIndex, AccountId)>', [new Uint8Array([ + const test = createTypeUnsafe(registry, 'Vec<(PropIndex, AccountId)>', [new Uint8Array([ 4, 10, 0, 0, 0, 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)[0] as Tuple; @@ -114,7 +114,7 @@ describe('Vec', (): void => { it('exposes a working indexOf', (): void => { expect(vector.indexOf('1')).toEqual(0); - expect(vector.indexOf(new Text('23'))).toEqual(1); + expect(vector.indexOf(new Text(registry, '23'))).toEqual(1); expect(vector.indexOf('0')).toEqual(-1); }); }); @@ -132,19 +132,19 @@ describe('Vec', (): void => { }); describe('utils', (): void => { - const vec = new Vec(Text, ['123', '456']); + const vec = new Vec(registry, Text, ['123', '456']); it('compares against codec types', (): void => { - expect(vec.eq([new Text('123'), new Text('456')])).toBe(true); + expect(vec.eq([new Text(registry, '123'), new Text(registry, '456')])).toBe(true); }); it('compares against codec + primitive types', (): void => { - expect(vec.eq(['123', new Text('456')])).toBe(true); + expect(vec.eq(['123', new Text(registry, '456')])).toBe(true); }); it('finds the index of an value', (): void => { const myId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; - const vec = new Vec(AccountId, [ + const vec = new Vec(registry, AccountId, [ '5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw', '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' ]); diff --git a/packages/types/src/codec/Vec.ts b/packages/types/src/codec/Vec.ts index 632524f83c48..d13dfb5bab07 100644 --- a/packages/types/src/codec/Vec.ts +++ b/packages/types/src/codec/Vec.ts @@ -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 { Codec, Constructor, InterfaceTypes } from '../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../types'; import { u8aToU8a, assert } from '@polkadot/util'; @@ -22,21 +22,21 @@ const MAX_LENGTH = 32768; export default class Vec extends AbstractArray { private _Type: Constructor; - constructor (Type: Constructor | InterfaceTypes, value: Vec | Uint8Array | string | any[] = [] as any[]) { - const Clazz = typeToConstructor(Type); + constructor (registry: Registry, Type: Constructor | InterfaceTypes, value: Vec | Uint8Array | string | any[] = [] as any[]) { + const Clazz = typeToConstructor(registry, Type); - super(...Vec.decodeVec(Clazz, value)); + super(registry, ...Vec.decodeVec(registry, Clazz, value)); this._Type = Clazz; } - public static decodeVec (Type: Constructor, value: Vec | Uint8Array | string | any[]): T[] { + public static decodeVec (registry: Registry, Type: Constructor, value: Vec | Uint8Array | string | any[]): T[] { if (Array.isArray(value)) { return value.map((entry, index): T => { try { return entry instanceof Type ? entry - : new Type(entry); + : new Type(registry, entry); } catch (error) { console.error(`Unable to decode Vec on index ${index}`, error.message); @@ -50,13 +50,13 @@ export default class Vec extends AbstractArray { assert(length.lten(MAX_LENGTH), `Vec length ${length.toString()} exceeds ${MAX_LENGTH}`); - return decodeU8a(u8a.subarray(offset), new Array(length.toNumber()).fill(Type)) as T[]; + return decodeU8a(registry, u8a.subarray(offset), new Array(length.toNumber()).fill(Type)) as T[]; } public static with (Type: Constructor | InterfaceTypes): Constructor> { return class extends Vec { - constructor (value?: any[]) { - super(Type, value); + constructor (registry: Registry, value?: any[]) { + super(registry, Type, value); } }; } @@ -75,7 +75,7 @@ export default class Vec extends AbstractArray { // convert type first, this removes overhead from the eq const other = _other instanceof this._Type ? _other - : new this._Type(_other); + : new this._Type(this.registry, _other); for (let i = 0; i < this.length; i++) { if (other.eq(this[i])) { @@ -90,6 +90,6 @@ export default class Vec extends AbstractArray { * @description Returns the base runtime type name for this instance */ public toRawType (): string { - return `Vec<${new this._Type().toRawType()}>`; + return `Vec<${new this._Type(this.registry).toRawType()}>`; } } diff --git a/packages/types/src/codec/VecFixed.spec.ts b/packages/types/src/codec/VecFixed.spec.ts index 8473e7f96e45..d9850faf033e 100644 --- a/packages/types/src/codec/VecFixed.spec.ts +++ b/packages/types/src/codec/VecFixed.spec.ts @@ -2,17 +2,20 @@ // 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 '../codec'; import Text from '../primitive/Text'; import VecFixed from './VecFixed'; describe('VecFixed', (): void => { + const registry = new TypeRegistry(); + describe('construction', (): void => { it('constructs via empty', (): void => { - expect(new VecFixed(Text, 2).toHex()).toEqual('0x0000'); + expect(new VecFixed(registry, Text, 2).toHex()).toEqual('0x0000'); }); it('constructs via Uint8Array', (): void => { - expect(new VecFixed(Text, 2, new Uint8Array([0x00, 0x04, 0x31])).toHex()).toEqual('0x000431'); + expect(new VecFixed(registry, Text, 2, new Uint8Array([0x00, 0x04, 0x31])).toHex()).toEqual('0x000431'); }); }); @@ -20,7 +23,7 @@ describe('VecFixed', (): void => { let test: VecFixed; beforeEach((): void => { - test = new (VecFixed.with(Text, 5))(['1', '2', '3', undefined, '5']); + test = new (VecFixed.with(Text, 5))(registry, ['1', '2', '3', undefined, '5']); }); it('has a sane string types', (): void => { diff --git a/packages/types/src/codec/VecFixed.ts b/packages/types/src/codec/VecFixed.ts index a9de9958317c..ca1f449fe372 100644 --- a/packages/types/src/codec/VecFixed.ts +++ b/packages/types/src/codec/VecFixed.ts @@ -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 { Codec, Constructor, InterfaceTypes } from '../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../types'; import { assert, isU8a, u8aConcat, compactToU8a } from '@polkadot/util'; import AbstractArray from './AbstractArray'; @@ -17,16 +17,17 @@ import Vec from './Vec'; export default class VecFixed extends AbstractArray { private _Type: Constructor; - constructor (Type: Constructor | InterfaceTypes, length: number, value: VecFixed | Uint8Array | string | any[] = [] as any[]) { - const Clazz = typeToConstructor(Type); + constructor (registry: Registry, Type: Constructor | InterfaceTypes, length: number, value: VecFixed | Uint8Array | string | any[] = [] as any[]) { + const Clazz = typeToConstructor(registry, Type); - super(...VecFixed.decodeVecFixed(Clazz, length, value)); + super(registry, ...VecFixed.decodeVecFixed(registry, Clazz, length, value)); this._Type = Clazz; } - public static decodeVecFixed (Type: Constructor, allocLength: number, value: VecFixed | Uint8Array | string | any[]): T[] { + public static decodeVecFixed (registry: Registry, Type: Constructor, allocLength: number, value: VecFixed | Uint8Array | string | any[]): T[] { const values = Vec.decodeVec( + registry, Type, isU8a(value) ? u8aConcat(compactToU8a(allocLength), value) @@ -34,7 +35,7 @@ export default class VecFixed extends AbstractArray { ); while (values.length < allocLength) { - values.push(new Type()); + values.push(new Type(registry)); } assert(values.length === allocLength, `Expected a length of exactly ${allocLength} entries`); @@ -44,8 +45,8 @@ export default class VecFixed extends AbstractArray { public static with (Type: Constructor | InterfaceTypes, length: number): Constructor> { return class extends VecFixed { - constructor (value?: any[]) { - super(Type, length, value); + constructor (registry: Registry, value?: any[]) { + super(registry, Type, length, value); } }; } @@ -54,7 +55,7 @@ export default class VecFixed extends AbstractArray { * @description The type for the items */ public get Type (): string { - return new this._Type().toRawType(); + return new this._Type(this.registry).toRawType(); } public toU8a (): Uint8Array { diff --git a/packages/types/src/codec/create/createClass.spec.ts b/packages/types/src/codec/create/createClass.spec.ts index a958c5db2c28..d6845bb593b9 100644 --- a/packages/types/src/codec/create/createClass.spec.ts +++ b/packages/types/src/codec/create/createClass.spec.ts @@ -2,22 +2,22 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createClass } from '.'; +import { createClass, TypeRegistry } from '.'; describe('createClass', (): void => { + const registry = new TypeRegistry(); + it('should memoize from strings', (): void => { - const a = createClass('BabeWeight'); - const b = createClass('BabeWeight'); + const a = createClass(registry, 'BabeWeight'); + const b = createClass(registry, 'BabeWeight'); expect(a).toBe(b); }); it('should return equivalents for Bytes & Vec', (): void => { - const A = createClass('Vec'); - const B = createClass('Bytes'); + const A = createClass(registry, 'Vec'); + const B = createClass(registry, 'Bytes'); - expect(new A() instanceof B).toBe(true); + expect(new A(registry) instanceof B).toBe(true); }); }); diff --git a/packages/types/src/codec/create/createClass.ts b/packages/types/src/codec/create/createClass.ts index d6d500661141..1b1b548fbd20 100644 --- a/packages/types/src/codec/create/createClass.ts +++ b/packages/types/src/codec/create/createClass.ts @@ -2,29 +2,27 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Codec, Constructor, InterfaceTypes } from '../../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../../types'; import { FromReg } from './types'; import { InterfaceRegistry } from '../../interfaceRegistry'; import { getTypeClass } from './getTypeClass'; import { getTypeDef } from './getTypeDef'; -export function createClass ( - type: K -): Constructor> { - return getTypeClass>(getTypeDef(type)); +export function createClass (registry: Registry, type: K): Constructor> { + return getTypeClass>(registry, getTypeDef(type)); } // An unsafe version of the `createType` below. It's unsafe because the `type` -// argument here can be any string, which, if not parseable, will yield a -// runtime error. -export function ClassOfUnsafe (name: K): Constructor> { - return createClass(name); +// argument here can be any string, which, if it cannot be parsed, it will yield +// a runtime error. +export function ClassOfUnsafe (registry: Registry, name: K): Constructor> { + return createClass(registry, name); } // alias for createClass -export function ClassOf (name: K): Constructor { +export function ClassOf (registry: Registry, name: K): Constructor { // TS2589: Type instantiation is excessively deep and possibly infinite. // The above happens with as Constructor; - return ClassOfUnsafe(name) as any; + return ClassOfUnsafe(registry, name) as any; } diff --git a/packages/types/src/codec/create/createType.spec.ts b/packages/types/src/codec/create/createType.spec.ts index 3f8d5082f2c5..5f3797e8e549 100644 --- a/packages/types/src/codec/create/createType.spec.ts +++ b/packages/types/src/codec/create/createType.spec.ts @@ -2,15 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createClass, createType, createTypeUnsafe, getTypeRegistry, ClassOf } from '.'; +import { createClass, createType, createTypeUnsafe, ClassOf, TypeRegistry } from '.'; import CodecSet from '../Set'; describe('createType', (): void => { + const registry = new TypeRegistry(); + it('allows creation of a Struct', (): void => { expect( - createTypeUnsafe('{"balance":"Balance","index":"u32"}', [{ + createTypeUnsafe(registry, '{"balance":"Balance","index":"u32"}', [{ balance: 1234, index: '0x10' }]).toJSON() @@ -22,68 +22,68 @@ describe('createType', (): void => { it('allows creation of a BTreeMap', (): void => { expect( - createTypeUnsafe('BTreeMap', ['0x041c62617a7a696e6745000000']).toString() + createTypeUnsafe(registry, 'BTreeMap', ['0x041c62617a7a696e6745000000']).toString() ).toEqual('{"bazzing":69}'); }); it('allows creation of a Result', (): void => { expect( - createTypeUnsafe('Result', ['0x011064656667']).toJSON() + createTypeUnsafe(registry, 'Result', ['0x011064656667']).toJSON() ).toEqual({ Error: 'defg' }); }); it('allows creation of a Tuple', (): void => { expect( - createTypeUnsafe('(Balance,u32)', [[1234, 5678]]).toJSON() + createTypeUnsafe(registry, '(Balance,u32)', [[1234, 5678]]).toJSON() ).toEqual([1234, 5678]); }); it('allows creation of a Enum (simple)', (): void => { expect( - createTypeUnsafe('{"_enum": ["A", "B", "C"]}', [1]).toJSON() + createTypeUnsafe(registry, '{"_enum": ["A", "B", "C"]}', [1]).toJSON() ).toEqual(1); }); it('allows creation of a Enum (parametrised)', (): void => { expect( - createTypeUnsafe('{"_enum": {"A": null, "B": "u32", "C": null} }', [1]).toJSON() + createTypeUnsafe(registry, '{"_enum": {"A": null, "B": "u32", "C": null} }', [1]).toJSON() ).toEqual({ B: 0 }); }); it('allows creation of a Set', (): void => { expect( - createTypeUnsafe('{"_set": { "A": 1, "B": 2, "C": 4, "D": 8, "E": 16, "G": 32, "H": 64, "I": 128 } }', [1 + 4 + 16 + 64]).strings + createTypeUnsafe(registry, '{"_set": { "A": 1, "B": 2, "C": 4, "D": 8, "E": 16, "G": 32, "H": 64, "I": 128 } }', [1 + 4 + 16 + 64]).strings ).toEqual(['A', 'C', 'E', 'H']); }); it('allows creation of a [u8; 8]', (): void => { expect( - createTypeUnsafe('[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex() + createTypeUnsafe(registry, '[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex() ).toEqual('0x1200230045006700'); }); it('allows creation of a [u16; 4]', (): void => { expect( - createTypeUnsafe('[u16; 4]', [[0x1200, 0x2300, 0x4500, 0x6700]]).toU8a() + createTypeUnsafe(registry, '[u16; 4]', [[0x1200, 0x2300, 0x4500, 0x6700]]).toU8a() ).toEqual(new Uint8Array([0x00, 0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67])); }); describe('instanceof', (): void => { it('instanceof should work (primitive type)', (): void => { - const value = createType('Balance', 1234); + const value = createType(registry, 'Balance', 1234); - expect(value instanceof ClassOf('Balance')).toBe(true); + expect(value instanceof ClassOf(registry, 'Balance')).toBe(true); }); it('instanceof should work (srml type)', (): void => { - const value = createType('Gas', 1234); - const Gas = ClassOf('Gas'); + const value = createType(registry, 'Gas', 1234); + const Gas = ClassOf(registry, 'Gas'); expect(value instanceof Gas).toBe(true); }); it('instanceof should work (complex type)', (): void => { - getTypeRegistry().register({ + registry.register({ TestComplex: { balance: 'Balance', accountId: 'AccountId', @@ -92,32 +92,32 @@ describe('createType', (): void => { } }); - const value = createTypeUnsafe('TestComplex', [{ + const value = createTypeUnsafe(registry, 'TestComplex', [{ balance: 123, accountId: '0x1234567812345678123456781234567812345678123456781234567812345678', log: [456, 789], fromSrml: 0 }]); - expect(value instanceof createClass('TestComplex')).toBe(true); + expect(value instanceof createClass(registry, 'TestComplex')).toBe(true); }); it('allows for re-registration of a type', (): void => { - const balDef = createType('Balance'); + const balDef = createType(registry, 'Balance'); - expect(balDef instanceof ClassOf('Balance')); + expect(balDef instanceof ClassOf(registry, 'Balance')); expect(balDef.bitLength()).toEqual(128); - getTypeRegistry().register({ Balance: 'u32' }); + registry.register({ Balance: 'u32' }); - const balu32 = createType('Balance'); + const balu32 = createType(registry, 'Balance'); - expect(balu32 instanceof ClassOf('Balance')); + expect(balu32 instanceof ClassOf(registry, 'Balance')); expect(balu32.bitLength()).toEqual(32); }); it('allows for re-registration of a type (affecting derives)', (): void => { - getTypeRegistry().register({ + registry.register({ Balance: 'u128', TestComplex: { balance: 'Balance', @@ -127,13 +127,13 @@ describe('createType', (): void => { } }); - const cmpDef: any = createTypeUnsafe('TestComplex'); + const cmpDef: any = createTypeUnsafe(registry, 'TestComplex'); expect(cmpDef.balance.bitLength()).toEqual(128); - getTypeRegistry().register({ Balance: 'u32' }); + registry.register({ Balance: 'u32' }); - const cmpu32: any = createTypeUnsafe('TestComplex'); + const cmpu32: any = createTypeUnsafe(registry, 'TestComplex'); expect(cmpu32.balance.bitLength()).toEqual(32); }); diff --git a/packages/types/src/codec/create/createType.ts b/packages/types/src/codec/create/createType.ts index ee00944b163a..2866be09e83f 100644 --- a/packages/types/src/codec/create/createType.ts +++ b/packages/types/src/codec/create/createType.ts @@ -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 { Codec, Constructor, InterfaceTypes } from '../../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../../types'; import { FromReg } from './types'; import { isU8a, u8aToHex } from '@polkadot/util'; @@ -43,8 +43,8 @@ function checkInstance (valu // 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 (Type: Constructor>, params: any[] = [], isPedantic?: boolean): FromReg { - const created = new Type(...params); +function initType (registry: Registry, Type: Constructor>, params: any[] = [], isPedantic?: boolean): FromReg { + const created = new Type(registry, ...params); const [value] = params; if (isPedantic && isU8a(value)) { @@ -57,9 +57,9 @@ function initType (Type: Con // 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 (type: K, params: any[] = [], isPedantic?: boolean): FromReg { +export function createTypeUnsafe (registry: Registry, type: K, params: any[] = [], isPedantic?: boolean): FromReg { try { - return initType(createClass(type), params, isPedantic); + return initType(registry, createClass(registry, type), params, isPedantic); } catch (error) { throw new Error(`createType(${type}):: ${error.message}`); } @@ -71,8 +71,8 @@ export function createTypeUnsafe (type: K, ...params: any[]): InterfaceRegistry[K] { +export function createType (registry: Registry, type: K, ...params: any[]): InterfaceRegistry[K] { // error TS2589: Type instantiation is excessively deep and possibly infinite. // The above happens with as Constructor; - return createTypeUnsafe(type, params) as any; + return createTypeUnsafe(registry, type, params) as any; } diff --git a/packages/types/src/codec/create/getTypeClass.spec.ts b/packages/types/src/codec/create/getTypeClass.spec.ts index e53eed731876..d79afd24b11a 100644 --- a/packages/types/src/codec/create/getTypeClass.spec.ts +++ b/packages/types/src/codec/create/getTypeClass.spec.ts @@ -2,16 +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 '../../injector'; - import { Codec, Constructor } from '../../types'; -import { getTypeClass } from '.'; +import { getTypeClass, TypeRegistry } from '.'; describe('getTypeClass', (): void => { + const registry = new TypeRegistry(); + it('does not allow invalid types', (): void => { expect( - (): Constructor => getTypeClass('SomethingInvalid' as any) + (): Constructor => getTypeClass(registry, 'SomethingInvalid' as any) ).toThrow(/determine type/); }); }); diff --git a/packages/types/src/codec/create/getTypeClass.ts b/packages/types/src/codec/create/getTypeClass.ts index 977ebd7059e8..0057966667ae 100644 --- a/packages/types/src/codec/create/getTypeClass.ts +++ b/packages/types/src/codec/create/getTypeClass.ts @@ -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 { Codec, Constructor, InterfaceTypes } from '../../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../../types'; import { TypeDef, TypeDefExtVecFixed, TypeDefInfo } from './types'; import { assert } from '@polkadot/util'; @@ -20,7 +20,6 @@ import U8aFixed, { BitLength as U8aFixedBitLength } from '../U8aFixed'; import Vec from '../Vec'; import VecFixed from '../VecFixed'; import { ClassOf } from './createClass'; -import { getTypeRegistry } from './registry'; function getSubDefArray (value: TypeDef): TypeDef[] { assert(value.sub && Array.isArray(value.sub), `Expected subtype as TypeDef[] in ${JSON.stringify(value)}`); @@ -56,39 +55,39 @@ function getTypeClassArray (value: TypeDef): (InterfaceTypes)[] { ); } -const infoMapping: Record Constructor> = { - [TypeDefInfo.BTreeMap]: (value: TypeDef): Constructor => { +const infoMapping: Record Constructor> = { + [TypeDefInfo.BTreeMap]: (registry: Registry, value: TypeDef): Constructor => { const [keyType, valueType] = getTypeClassArray(value); // eslint-disable-next-line @typescript-eslint/no-use-before-define return BTreeMap.with(keyType, valueType); }, - [TypeDefInfo.Compact]: (value: TypeDef): Constructor => Compact.with(getSubType(value)), + [TypeDefInfo.Compact]: (registry: Registry, value: TypeDef): Constructor => Compact.with(getSubType(value)), // eslint-disable-next-line @typescript-eslint/no-use-before-define - [TypeDefInfo.DoubleMap]: (value: TypeDef): Constructor => getTypeClass(getSubDef(value)), + [TypeDefInfo.DoubleMap]: (registry: Registry, value: TypeDef): Constructor => getTypeClass(registry, getSubDef(value)), - [TypeDefInfo.Enum]: (value: TypeDef): Constructor => Enum.with(getTypeClassMap(value)), + [TypeDefInfo.Enum]: (registry: Registry, value: TypeDef): Constructor => Enum.with(getTypeClassMap(value)), - [TypeDefInfo.Linkage]: (value: TypeDef): Constructor => Linkage.withKey(getSubType(value)), + [TypeDefInfo.Linkage]: (registry: Registry, value: TypeDef): Constructor => Linkage.withKey(getSubType(value)), // eslint-disable-next-line @typescript-eslint/no-unused-vars - [TypeDefInfo.Null]: (_: TypeDef): Constructor => ClassOf('Null'), + [TypeDefInfo.Null]: (registry: Registry, _: TypeDef): Constructor => ClassOf(registry, 'Null'), - [TypeDefInfo.Option]: (value: TypeDef): Constructor => Option.with(getSubType(value)), + [TypeDefInfo.Option]: (registry: Registry, value: TypeDef): Constructor => Option.with(getSubType(value)), - [TypeDefInfo.Plain]: (value: TypeDef): Constructor => - getTypeRegistry().getOrThrow(value.type, `Unable to find plain type for ${JSON.stringify(value)}`), + [TypeDefInfo.Plain]: (registry: Registry, value: TypeDef): Constructor => + registry.getOrThrow(value.type, `Unable to find plain type for ${JSON.stringify(value)}`), - [TypeDefInfo.Result]: (value: TypeDef): Constructor => { + [TypeDefInfo.Result]: (registry: Registry, value: TypeDef): Constructor => { const [Ok, Error] = getTypeClassArray(value); // eslint-disable-next-line @typescript-eslint/no-use-before-define return Result.with({ Ok, Error }); }, - [TypeDefInfo.Set]: (value: TypeDef): Constructor => { + [TypeDefInfo.Set]: (registry: Registry, value: TypeDef): Constructor => { const result: Record = {}; return CodecSet.with( @@ -100,21 +99,21 @@ const infoMapping: Record Constructor> = { ); }, - [TypeDefInfo.Struct]: (value: TypeDef): Constructor => Struct.with(getTypeClassMap(value)), + [TypeDefInfo.Struct]: (registry: Registry, value: TypeDef): Constructor => Struct.with(getTypeClassMap(value)), - [TypeDefInfo.Tuple]: (value: TypeDef): Constructor => Tuple.with(getTypeClassArray(value)), + [TypeDefInfo.Tuple]: (registry: Registry, value: TypeDef): Constructor => Tuple.with(getTypeClassArray(value)), - [TypeDefInfo.Vec]: (value: TypeDef): Constructor => { + [TypeDefInfo.Vec]: (registry: Registry, value: TypeDef): Constructor => { const subType = getSubType(value); return ( subType === 'u8' - ? ClassOf('Bytes') + ? ClassOf(registry, 'Bytes') : Vec.with(subType) ); }, - [TypeDefInfo.VecFixed]: (value: TypeDef): Constructor => { + [TypeDefInfo.VecFixed]: (registry: Registry, value: TypeDef): Constructor => { assert(value.ext, 'Expected length & type information for fixed vector'); const ext = value.ext as TypeDefExtVecFixed; @@ -128,8 +127,8 @@ const infoMapping: Record Constructor> = { }; // Returns the type Class for construction -export function getTypeClass (value: TypeDef): Constructor { - const Type = getTypeRegistry().get(value.type); +export function getTypeClass (registry: Registry, value: TypeDef): Constructor { + const Type = registry.get(value.type); if (Type) { return Type; @@ -141,5 +140,5 @@ export function getTypeClass (value: TypeDef): Construc throw new Error(`Unable to determine type from ${JSON.stringify(value)}`); } - return getFn(value) as Constructor; + return getFn(registry, value) as Constructor; } diff --git a/packages/types/src/codec/create/getTypeDef.spec.ts b/packages/types/src/codec/create/getTypeDef.spec.ts index cd42b710defd..d3954f731425 100644 --- a/packages/types/src/codec/create/getTypeDef.spec.ts +++ b/packages/types/src/codec/create/getTypeDef.spec.ts @@ -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 '../../injector'; - import { TypeDef, TypeDefInfo } from '../types'; import { getTypeDef } from '.'; diff --git a/packages/types/src/codec/create/registry.spec.ts b/packages/types/src/codec/create/registry.spec.ts index 8108d6af377a..285f78256c20 100644 --- a/packages/types/src/codec/create/registry.spec.ts +++ b/packages/types/src/codec/create/registry.spec.ts @@ -2,11 +2,9 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import { Constructor } from '../../types'; -import { TypeRegistry, getTypeRegistry } from './registry'; +import { TypeRegistry } from './registry'; import Struct from '../Struct'; import Text from '../../primitive/Text'; import U32 from '../../primitive/U32'; @@ -20,11 +18,7 @@ function isChildClass (Parent: Constructor, Child?: Constructor): bool } describe('TypeRegistry', (): void => { - let registry: TypeRegistry; - - beforeEach((): void => { - registry = getTypeRegistry(); - }); + const registry = new TypeRegistry(); it('handles non exist type', (): void => { expect(registry.get('non-exist')).toBeUndefined(); @@ -64,8 +58,8 @@ describe('TypeRegistry', (): void => { expect(registry.hasClass('Recursive')).toBe(true); - const last = new Recursive({ next: null }); - const first = new Recursive({ next: last }); + const last = new Recursive(registry, { next: null }); + const first = new Recursive(registry, { next: last }); expect((first as any).next.isSome).toBe(true); expect((first as any).next.unwrap().next.isSome).toBe(false); @@ -89,8 +83,8 @@ describe('TypeRegistry', (): void => { expect(registry.hasClass('Recursive')).toBe(true); - const last = new B({ End: null }); - const first = new B({ Other: new A({ next: last }) }); + const last = new B(registry, { End: null }); + const first = new B(registry, { Other: new A(registry, { next: last }) }); expect((first as any).isOther).toBe(true); }); @@ -102,7 +96,7 @@ describe('TypeRegistry', (): void => { const Type = registry.getOrThrow('U32Renamed'); - expect(new Type() instanceof U32).toBe(true); + expect(new Type(registry) instanceof U32).toBe(true); }); it('can create structs via definition', (): void => { @@ -113,8 +107,8 @@ describe('TypeRegistry', (): void => { } }); - const SomeStruct: any = registry.get('SomeStruct'); - const struct: any = new SomeStruct({ + const SomeStruct = registry.getOrThrow('SomeStruct'); + const struct: any = new SomeStruct(registry, { foo: 42, bar: 'testing' }); diff --git a/packages/types/src/codec/create/registry.ts b/packages/types/src/codec/create/registry.ts index dfa0ca0e9139..e369256230d7 100644 --- a/packages/types/src/codec/create/registry.ts +++ b/packages/types/src/codec/create/registry.ts @@ -1,60 +1,60 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ // Copyright 2017-2019 @polkadot/types authors & contributors // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Codec, Constructor, RegistryTypes } from '../../types'; +import { CallFunction, Codec, Constructor, RegistryTypes, Registry, RegistryMetadata, TypeDef } from '../../types'; -import { isFunction, isString, isUndefined } from '@polkadot/util'; +import extrinsicsFromMeta from '@polkadot/metadata/Decorated/extrinsics/fromMetadata'; +import { assert, isFunction, isString, isUndefined, stringCamelCase, u8aToHex } from '@polkadot/util'; +import { EventData } from '../../primitive/Generic/Event'; import { createClass } from './createClass'; +import { getTypeClass } from './getTypeClass'; +import { getTypeDef } from './getTypeDef'; -export class TypeRegistry { - public static readonly defaultRegistry: TypeRegistry = new TypeRegistry(); +const FN_UNKNOWN: Partial = { + method: 'unknown', + section: 'unknown' +}; +export class TypeRegistry implements Registry { private _classes: Map = new Map(); private _definitions: Map = new Map(); - public register (type: Constructor | RegistryTypes): void; + private _metadataCalls: Record = {}; - // eslint-disable-next-line no-dupe-class-members - public register (name: string, type: Constructor): void; + private _metadataEvents: Record> = {}; - // eslint-disable-next-line no-dupe-class-members - public register (arg1: string | Constructor | RegistryTypes, arg2?: Constructor): void { - if (isString(arg1)) { - const name = arg1; - const type = arg2!; + constructor () { + // we only want to import these on creation, i.e. we want to avoid types + // weird side-effects from circular references. (Since registry is injected + // into types, this can be a real concern now) + const baseTypes: RegistryTypes = require('../../index.types'); + const definitions: Record = require('../../interfaces/definitions'); - this._classes.set(name, type); - } else if (isFunction(arg1)) { - const name = arg1.name; - const type = arg1; + // since these are classes, they are injected first + this.register({ ...baseTypes }); - this._classes.set(name, type); - } else { - this.registerObject(arg1); - } + // since these are definitions, they would only get created when needed + Object.values(definitions).forEach(({ types }): void => + this.register(types) + ); } - private registerObject (obj: RegistryTypes): void { - Object.entries(obj).forEach(([name, type]): void => { - if (isFunction(type)) { - // This _looks_ a bit funny, but `typeof Clazz === 'function' - this._classes.set(name, type); - } else { - const def = isString(type) - ? type - : JSON.stringify(type); + public findMetaCall (callIndex: Uint8Array): CallFunction { + assert(Object.keys(this._metadataCalls).length > 0, 'Calling registry.findMetaCall before metadata has been attached.'); - // we already have this type, remove the classes registered for it - if (this._classes.has(name)) { - this._classes.delete(name); - } + return this._metadataCalls[callIndex.toString()] || FN_UNKNOWN; + } - this._definitions.set(name, def); - } - }); + public findMetaEvent (eventIndex: Uint8Array): Constructor { + const Event = this._metadataEvents[eventIndex.toString()]; + + assert(!isUndefined(Event), `Unable to find Event with index ${u8aToHex(eventIndex)}`); + + return Event; } public get (name: string): Constructor | undefined { @@ -66,10 +66,11 @@ export class TypeRegistry { // we have a definition, so create the class now (lazily) if (definition) { - const BaseType = createClass(definition); + const BaseType = createClass(this, definition); // NOTE If we didn't extend here, we would have strange artifacts. An example is // Balance, with this, new Balance() instanceof u128 is true, but Balance !== u128 + // Additionally, we now pass through the registry, which is a link to ourselves Type = class extends BaseType {}; this._classes.set(name, Type); @@ -104,8 +105,75 @@ export class TypeRegistry { public hasType (name: string): boolean { return this.hasClass(name) || this.hasDef(name); } -} -export function getTypeRegistry (): TypeRegistry { - return TypeRegistry.defaultRegistry; + public register (type: Constructor | RegistryTypes): void; + + // eslint-disable-next-line no-dupe-class-members + public register (name: string, type: Constructor): void; + + // eslint-disable-next-line no-dupe-class-members + public register (arg1: string | Constructor | RegistryTypes, arg2?: Constructor): void { + // NOTE Constructors appear as functions here + if (isFunction(arg1)) { + this._classes.set(arg1.name, arg1); + } else if (isString(arg1)) { + assert(isFunction(arg2), `Expected class definition passed to '${arg1}' registration`); + + this._classes.set(arg1, arg2); + } else { + this.registerObject(arg1); + } + } + + private registerObject (obj: RegistryTypes): void { + Object.entries(obj).forEach(([name, type]): void => { + if (isFunction(type)) { + // This _looks_ a bit funny, but `typeof Clazz === 'function' + this._classes.set(name, type); + } else { + const def = isString(type) + ? type + : JSON.stringify(type); + + // we already have this type, remove the classes registered for it + if (this._classes.has(name)) { + this._classes.delete(name); + } + + this._definitions.set(name, def); + } + }); + } + + // sets the metadata + public setMetadata (metadata: RegistryMetadata): void { + const extrinsics = extrinsicsFromMeta(this, metadata); + + // decorate the extrinsics + Object.values(extrinsics).forEach((methods): void => + Object.values(methods).forEach((method): void => { + this._metadataCalls[method.callIndex.toString()] = method; + }) + ); + + // decorate the events + metadata.asLatest.modules + .filter(({ events }): boolean => events.isSome) + .forEach((section, sectionIndex): void => { + const sectionName = stringCamelCase(section.name.toString()); + + 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.toString())); + const Types = typeDef.map((typeDef): Constructor => getTypeClass(this, typeDef)); + + this._metadataEvents[eventIndex.toString()] = class extends EventData { + constructor (registry: Registry, value: Uint8Array) { + super(registry, Types, value, typeDef, meta, sectionName, methodName); + } + }; + }); + }); + } } diff --git a/packages/types/src/codec/create/typeSplit.spec.ts b/packages/types/src/codec/create/typeSplit.spec.ts index bd026b83e515..0a5ba68fc502 100644 --- a/packages/types/src/codec/create/typeSplit.spec.ts +++ b/packages/types/src/codec/create/typeSplit.spec.ts @@ -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 '../../injector'; - import { typeSplit } from '.'; describe('typeSplit', (): void => { diff --git a/packages/types/src/codec/utils/compareArray.spec.ts b/packages/types/src/codec/utils/compareArray.spec.ts index 8dbeb870ad24..b86f0a9e246b 100644 --- a/packages/types/src/codec/utils/compareArray.spec.ts +++ b/packages/types/src/codec/utils/compareArray.spec.ts @@ -2,11 +2,13 @@ // 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 U32 from '../../primitive/U32'; import compareArray from './compareArray'; describe('compareArray', (): void => { - const a = [new U32(123), new U32(456), new U32(789)]; + const registry = new TypeRegistry(); + const a = [new U32(registry, 123), new U32(registry, 456), new U32(registry, 789)]; it('returns false when second param is a non-array', (): void => { expect( @@ -22,7 +24,7 @@ describe('compareArray', (): void => { it('compares array of codec agains codec', (): void => { expect( - compareArray(a, [new U32(123), new U32(456), new U32(789)]) + compareArray(a, [new U32(registry, 123), new U32(registry, 456), new U32(registry, 789)]) ).toBe(true); }); diff --git a/packages/types/src/codec/utils/compareMap.spec.ts b/packages/types/src/codec/utils/compareMap.spec.ts index e1741fad195c..a778c0bf23ff 100644 --- a/packages/types/src/codec/utils/compareMap.spec.ts +++ b/packages/types/src/codec/utils/compareMap.spec.ts @@ -3,14 +3,16 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Codec } from '../../types'; +import { TypeRegistry } from '../create'; import U32 from '../../primitive/U32'; import compareMap from './compareMap'; describe('compareMap', (): void => { + const registry = new TypeRegistry(); const a = new Map([ - ['decimals', new U32(15)], - ['missing', new U32(10)], - ['foobar', new U32(5)] + ['decimals', new U32(registry, 15)], + ['missing', new U32(registry, 10)], + ['foobar', new U32(registry, 5)] ]); const b = new Map([ ['decimals', 15], @@ -18,9 +20,9 @@ describe('compareMap', (): void => { ['foobar', 5] ]); const c = new Map([ - ['decimals', new U32(15)], - ['missing', new U32(10)], - ['foobar', new U32(5)] + ['decimals', new U32(registry, 15)], + ['missing', new U32(registry, 10)], + ['foobar', new U32(registry, 5)] ]); it('compares Map against Object', (): void => { diff --git a/packages/types/src/codec/utils/decodeU8a.ts b/packages/types/src/codec/utils/decodeU8a.ts index 50e8629f87d9..152ee21d9935 100644 --- a/packages/types/src/codec/utils/decodeU8a.ts +++ b/packages/types/src/codec/utils/decodeU8a.ts @@ -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 { Codec, Constructor } from '../../types'; +import { Codec, Constructor, Registry } from '../../types'; /** * Given an u8a, and an array of Type constructors, decode the u8a against the @@ -11,7 +11,7 @@ import { Codec, Constructor } from '../../types'; * @param u8a - The u8a to decode. * @param types - The array of Constructor to decode the U8a against. */ -export default function decodeU8a (u8a: Uint8Array, _types: Constructor[] | { [index: string]: Constructor }): Codec[] { +export default function decodeU8a (registry: Registry, u8a: Uint8Array, _types: Constructor[] | { [index: string]: Constructor }): Codec[] { const types = Array.isArray(_types) ? _types : Object.values(_types); @@ -21,7 +21,7 @@ export default function decodeU8a (u8a: Uint8Array, _types: Constructor[] | { [i } const Type = types[0]; - const value = new Type(u8a); + const value = new Type(registry, u8a); - return [value].concat(decodeU8a(u8a.subarray(value.encodedLength), types.slice(1))); + return [value].concat(decodeU8a(registry, u8a.subarray(value.encodedLength), types.slice(1))); } diff --git a/packages/types/src/codec/utils/mapToTypeMap.ts b/packages/types/src/codec/utils/mapToTypeMap.ts index f55ff251a115..008ea7bd0ebd 100644 --- a/packages/types/src/codec/utils/mapToTypeMap.ts +++ b/packages/types/src/codec/utils/mapToTypeMap.ts @@ -2,29 +2,29 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Codec, Constructor, InterfaceTypes } from '../../types'; +import { Codec, Constructor, InterfaceTypes, Registry } from '../../types'; import { isString } from '@polkadot/util'; import { ClassOf } from '../create'; -export function typeToConstructor (type: InterfaceTypes | Constructor): Constructor { +export function typeToConstructor (registry: Registry, type: InterfaceTypes | Constructor): Constructor { return ( isString(type) - ? ClassOf(type) + ? ClassOf(registry, type) : type ) as Constructor; } /** - * @description takes an input map of the form `{ [string]: string | Constructor }` and returns a map of `{ [string]: Conbstructor }` + * @description takes an input map of the form `{ [string]: string | Constructor }` and returns a map of `{ [string]: Constructor }` */ -export function mapToTypeMap (input: Record): Record { - const output: Record = {}; +export function mapToTypeMap (registry: Registry, input: Record): Record { + return Object + .entries(input) + .reduce((output: Record, [key, type]): Record => { + output[key] = typeToConstructor(registry, type); - Object.entries(input).forEach(([key, type]): void => { - output[key] = typeToConstructor(type); - }); - - return output; + return output; + }, {}); } diff --git a/packages/types/src/index.spec.ts b/packages/types/src/index.spec.ts index a73ce9b09323..d79aedbf6cec 100644 --- a/packages/types/src/index.spec.ts +++ b/packages/types/src/index.spec.ts @@ -2,13 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import './injector'; - import Metadata from '@polkadot/metadata/Metadata'; import metadataStatic from '@polkadot/metadata/Metadata/static'; -import { createTypeUnsafe } from './codec/create'; -import GenericCall from './primitive/Generic/Call'; +import { createTypeUnsafe, TypeRegistry } from './codec/create'; import { Codec } from './types'; import * as exported from './index.types'; import * as definitions from './interfaces/definitions'; @@ -24,13 +21,15 @@ const UNCONSTRUCTABLE = [ 'usize' ].map((v): string => v.toLowerCase()); +const registry = new TypeRegistry(); + function testTypes (type: string, typeNames: string[]): void { describe(type, (): void => { describe(`${type}:: default creation`, (): void => { typeNames.forEach((name): void => { it(`creates an empty ${name}`, (): void => { const constructFn = (): Codec => - createTypeUnsafe(name); + createTypeUnsafe(registry, name); if (UNCONSTRUCTABLE.includes(name.toLowerCase())) { expect(constructFn).toThrow(); @@ -42,12 +41,13 @@ function testTypes (type: string, typeNames: string[]): void { }); describe(`${type}:: default creation (empty bytes)`, (): void => { - GenericCall.injectMetadata(new Metadata(metadataStatic)); + // eslint-disable-next-line no-new + new Metadata(registry, metadataStatic); typeNames.forEach((name): void => { it(`creates an empty ${name} (from empty bytes)`, (): void => { const constructFn = (): Codec => - createTypeUnsafe(name, [createTypeUnsafe('Bytes')]); + createTypeUnsafe(registry, name, [createTypeUnsafe(registry, 'Bytes')]); if (UNCONSTRUCTABLE.includes(name.toLowerCase())) { expect(constructFn).toThrow(); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index d164e9de296a..6e776d739efe 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -7,18 +7,6 @@ import { assertSingletonPackage } from '@polkadot/util'; -// HACK Register our types before anything else - if just means that any use of -// createType (either here, like in derive) or in users of this app, will have -// the type available without resorting to juggling imports themselves. -// -// This was found here in api-derive (see ReferendumInfoExtended) as well in apps, -// where `createType` was used on constant definitions, but before the actual API -// has been injected (which registers the srml types). -// -// It needs resolution, but atm, no worse than having classes that are statically -// defined, so really, eating the elephant one bite at a time... -import './injector'; - assertSingletonPackage('@polkadot/types'); export * from './codec'; diff --git a/packages/types/src/injector.ts b/packages/types/src/injector.ts deleted file mode 100644 index 57054e7e976e..000000000000 --- a/packages/types/src/injector.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017-2019 @polkadot/types authors & contributors -// This software may be modified and distributed under the terms -// of the Apache-2.0 license. See the LICENSE file for details. - -import { getTypeRegistry } from './codec/create'; -import * as definitions from './interfaces/definitions'; -import * as baseTypes from './index.types'; - -/** - * @description A utility method that injects all the srml definitions into the type registry - */ -export function injectTypes (): void { - const registry = getTypeRegistry(); - - // since these are classes, the are active immediately - registry.register({ ...baseTypes }); - - // since these are definitions, they would only get created when needed - Object.values(definitions).forEach(({ types }): void => - registry.register(types) - ); -} - -injectTypes(); diff --git a/packages/types/src/interfaces/rpc/ChainProperties.spec.ts b/packages/types/src/interfaces/rpc/ChainProperties.spec.ts index 0616dccca3e1..fe5fac9114da 100644 --- a/packages/types/src/interfaces/rpc/ChainProperties.spec.ts +++ b/packages/types/src/interfaces/rpc/ChainProperties.spec.ts @@ -2,19 +2,19 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; describe('ChainProperties', (): void => { + const registry = new TypeRegistry(); + it('decodes from a null value', (): void => { expect( - [...createType('ChainProperties', null).entries()] + [...createType(registry, 'ChainProperties', null).entries()] ).toEqual([]); }); it('decodes from an actual object', (): void => { - const { ss58Format, tokenDecimals, tokenSymbol } = createType('ChainProperties', { + const { ss58Format, tokenDecimals, tokenSymbol } = createType(registry, 'ChainProperties', { ss58Format: 2, tokenDecimals: 15, tokenSymbol: 'KSM' @@ -26,7 +26,7 @@ describe('ChainProperties', (): void => { }); it('decodes from an object, flagged for non-existent ss58Format', (): void => { - const { ss58Format, tokenDecimals, tokenSymbol } = createType('ChainProperties', { tokenSymbol: 'DEV' }); + const { ss58Format, tokenDecimals, tokenSymbol } = createType(registry, 'ChainProperties', { tokenSymbol: 'DEV' }); expect(ss58Format.isNone).toBe(true); expect(tokenDecimals.isNone).toBe(true); diff --git a/packages/types/src/interfaces/rpc/ExtrinsicStatus.spec.ts b/packages/types/src/interfaces/rpc/ExtrinsicStatus.spec.ts index bd70ff808c97..c447e507cee6 100644 --- a/packages/types/src/interfaces/rpc/ExtrinsicStatus.spec.ts +++ b/packages/types/src/interfaces/rpc/ExtrinsicStatus.spec.ts @@ -2,18 +2,17 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import { ExtrinsicStatus } from './types'; -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; import rpc from '../../json/ExtrinsicStatus.001.json'; describe('ExtrinsicStatus', (): void => { + const registry = new TypeRegistry(); let status: ExtrinsicStatus; beforeEach((): void => { - status = createType('ExtrinsicStatus', rpc.params.result); + status = createType(registry, 'ExtrinsicStatus', rpc.params.result); }); it('has the correct type', (): void => { diff --git a/packages/types/src/interfaces/rpc/KeyValueOption.spec.ts b/packages/types/src/interfaces/rpc/KeyValueOption.spec.ts index d9bafa043f9a..051c13c07358 100644 --- a/packages/types/src/interfaces/rpc/KeyValueOption.spec.ts +++ b/packages/types/src/interfaces/rpc/KeyValueOption.spec.ts @@ -2,13 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; describe('KeyValueOption', (): void => { + const registry = new TypeRegistry(); + it('exposes the properties for key/value', (): void => { - const [key, value] = createType('KeyValueOption', [ + const [key, value] = createType(registry, 'KeyValueOption', [ '0x11223344' ]); diff --git a/packages/types/src/interfaces/rpc/RuntimeVersion.spec.ts b/packages/types/src/interfaces/rpc/RuntimeVersion.spec.ts index 6cb5316ec72d..21254f50b145 100644 --- a/packages/types/src/interfaces/rpc/RuntimeVersion.spec.ts +++ b/packages/types/src/interfaces/rpc/RuntimeVersion.spec.ts @@ -2,18 +2,17 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import { RuntimeVersion } from './types'; -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; import rpc from '../../json/RuntimeVersion.002.json'; describe('RuntimeVersion', (): void => { + const registry = new TypeRegistry(); let version: RuntimeVersion; beforeEach((): void => { - version = createType('RuntimeVersion', rpc.result); + version = createType(registry, 'RuntimeVersion', rpc.result); }); it('has the correct authoring', (): void => { diff --git a/packages/types/src/interfaces/rpc/StorageChangeSet.spec.ts b/packages/types/src/interfaces/rpc/StorageChangeSet.spec.ts index 6905f0764d79..2b67f4f1fc02 100644 --- a/packages/types/src/interfaces/rpc/StorageChangeSet.spec.ts +++ b/packages/types/src/interfaces/rpc/StorageChangeSet.spec.ts @@ -2,15 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createType } from '@polkadot/types'; +import { createType, TypeRegistry } from '@polkadot/types'; import json from '../../json/StorageChangeSet.001.json'; describe('StorageChangeSet', (): void => { + const registry = new TypeRegistry(); + describe('construction', (): void => { - const set = createType('StorageChangeSet', { + const set = createType(registry, 'StorageChangeSet', { block: '0x1234', changes: [ ['0xab', '0xcd'] @@ -31,7 +31,7 @@ describe('StorageChangeSet', (): void => { }); describe('json', (): void => { - const set = createType('StorageChangeSet', json.params.result); + const set = createType(registry, 'StorageChangeSet', json.params.result); it('has the correct hash', (): void => { expect( diff --git a/packages/types/src/interfaces/runtime/EdcsaSignature.spec.ts b/packages/types/src/interfaces/runtime/EdcsaSignature.spec.ts index 7bfaeeff72c0..19d86862c2d3 100644 --- a/packages/types/src/interfaces/runtime/EdcsaSignature.spec.ts +++ b/packages/types/src/interfaces/runtime/EdcsaSignature.spec.ts @@ -2,35 +2,33 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import { hexToU8a } from '@polkadot/util'; -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; const TESTS = [ - // jaco's claims sig '0x55bd020bdbbdc02de34e915effc9b18a99002f4c29f64e22e8dcbb69e722ea6c28e1bb53b9484063fbbfd205e49dcc1f620929f520c9c4c3695150f05a28f52a01', - // keith's claim sig '0x98adffe14b1882ba5a861d6aaa10805d52aed56f480e1ece01505a77470f29f15cb4b0a1dc33177761de8270199282baf160f255e1ca0e4c8354b54b0059e40a1c' ]; describe('EdcsaSignature', (): void => { + const registry = new TypeRegistry(); + it('constructs and has a valid representation (toHex)', (): void => { expect( - createType('EcdsaSignature', TESTS[0]).toHex() + createType(registry, 'EcdsaSignature', TESTS[0]).toHex() ).toEqual(TESTS[0]); }); it('constructs and has a valid representation (toJSON)', (): void => { expect( - createType('EcdsaSignature', TESTS[0]).toJSON() + createType(registry, 'EcdsaSignature', TESTS[0]).toJSON() ).toEqual(TESTS[0]); }); it('constructs and has a valid representation (toU8a)', (): void => { expect( - createType('EcdsaSignature', TESTS[1]).toU8a() + createType(registry, 'EcdsaSignature', TESTS[1]).toU8a() ).toEqual(hexToU8a(TESTS[1])); }); }); diff --git a/packages/types/src/interfaces/runtime/Header.spec.ts b/packages/types/src/interfaces/runtime/Header.spec.ts index 3ac3c2634125..9bba4b8ded38 100644 --- a/packages/types/src/interfaces/runtime/Header.spec.ts +++ b/packages/types/src/interfaces/runtime/Header.spec.ts @@ -2,11 +2,9 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import BN from 'bn.js'; -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; import json1 from '../../json/Header.001.json'; import json2 from '../../json/Header.002.json'; import json3 from '../../json/Header.003.json'; @@ -14,8 +12,10 @@ import block00300 from '../../json/SignedBlock.003.00.json'; import block00301 from '../../json/SignedBlock.003.01.json'; describe('Header', (): void => { + const registry = new TypeRegistry(); + it('decodes an actual JSON response', (): void => { - const header = createType('Header', json1.result); + const header = createType(registry, 'Header', json1.result); expect( header.number.toNumber() @@ -35,7 +35,7 @@ describe('Header', (): void => { }); it('parses old-style JSON headers (deprecated)', (): void => { - const header = createType('Header', json2.result); + const header = createType(registry, 'Header', json2.result); expect( header.digest.logs @@ -43,7 +43,7 @@ describe('Header', (): void => { }); it('creates a valid hash (incl. digest & compact)', (): void => { - const header = createType('Header', json3.result); + const header = createType(registry, 'Header', json3.result); expect( header.hash.toHex() @@ -54,7 +54,7 @@ describe('Header', (): void => { }); it('calculates correct hash, matching with parentHash', (): void => { - const blockHash = createType('Header', block00300.result.block.header).hash.toHex(); + const blockHash = createType(registry, 'Header', block00300.result.block.header).hash.toHex(); expect(blockHash).toEqual(block00301.result.block.header.parentHash); }); diff --git a/packages/types/src/interfaces/runtime/KeyValue.spec.ts b/packages/types/src/interfaces/runtime/KeyValue.spec.ts index 929118e79b35..eb4620b63a61 100644 --- a/packages/types/src/interfaces/runtime/KeyValue.spec.ts +++ b/packages/types/src/interfaces/runtime/KeyValue.spec.ts @@ -2,14 +2,14 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; describe('KeyValue', (): void => { + const registry = new TypeRegistry(); + it('decodes KeyValue from u8a', (): void => { expect( - createType('KeyValue', Uint8Array.from([ + createType(registry, 'KeyValue', Uint8Array.from([ 4 << 2, 0x11, 0x22, 0x33, 0x44, 9 << 2, @@ -20,7 +20,7 @@ describe('KeyValue', (): void => { it('encodes KeyValue from JSON', (): void => { expect( - createType('KeyValue', ['0x11223344', '0x998877665544332211']).toU8a() + createType(registry, 'KeyValue', ['0x11223344', '0x998877665544332211']).toU8a() ).toEqual( new Uint8Array([ 4 << 2, @@ -32,7 +32,7 @@ describe('KeyValue', (): void => { }); it('exposes the properties for key/value', (): void => { - const [key, value] = createType('KeyValue', ['0x11223344', '0x998877665544332211']); + const [key, value] = createType(registry, 'KeyValue', ['0x11223344', '0x998877665544332211']); expect(key.toHex()).toEqual('0x11223344'); expect(value.toHex()).toEqual('0x998877665544332211'); diff --git a/packages/types/src/interfaces/runtime/SignedBlock.spec.ts b/packages/types/src/interfaces/runtime/SignedBlock.spec.ts index 9a2d3b677a18..c27df4bd6584 100644 --- a/packages/types/src/interfaces/runtime/SignedBlock.spec.ts +++ b/packages/types/src/interfaces/runtime/SignedBlock.spec.ts @@ -2,25 +2,23 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import Metadata from '@polkadot/metadata/Metadata'; import metadataStatic from '@polkadot/metadata/Metadata/static'; -import { createType } from '../../codec/create'; -import Call from '../../primitive/Generic/Call'; +import { createType, TypeRegistry } from '../../codec/create'; import events from '../../json/SignedBlock.002.json'; import immortalTxs from '../../json/SignedBlock.004.immortal.json'; import mortalTxs from '../../json/SignedBlock.004.mortal.json'; import knownMehods from '../../json/SignedBlock.005.json'; -describe('SignedBlock', (): void => { - beforeEach((): void => { - Call.injectMetadata(new Metadata(metadataStatic)); - }); +const registry = new TypeRegistry(); +// eslint-disable-next-line no-new +new Metadata(registry, metadataStatic); + +describe('SignedBlock', (): void => { it('decodes a full block', (): void => { - const s = createType('SignedBlock', events.result); + const s = createType(registry, 'SignedBlock', events.result); expect( s.block.header.stateRoot.toString() @@ -40,7 +38,7 @@ describe('SignedBlock', (): void => { // Test to replicate https://github.com/polkadot-js/api/issues/1212 it('decodes to known extrinsics', (): void => { - const s = createType('SignedBlock', knownMehods.result); + const s = createType(registry, 'SignedBlock', knownMehods.result); const indexes = s.block.extrinsics.map(({ method: { callIndex } }): Uint8Array => callIndex); expect(indexes).toEqual([ @@ -51,7 +49,7 @@ describe('SignedBlock', (): void => { describe('eras', (): void => { it('can decode immortals', (): void => { - const s = createType('SignedBlock', immortalTxs.result); + const s = createType(registry, 'SignedBlock', immortalTxs.result); const immortalTx = s.block.extrinsics[0]; expect(immortalTx.method.methodName).toEqual('transfer'); @@ -59,7 +57,7 @@ describe('SignedBlock', (): void => { }); it('can decode mortals', (): void => { - const s = createType('SignedBlock', mortalTxs.result); + const s = createType(registry, 'SignedBlock', mortalTxs.result); const mortalTx = s.block.extrinsics[0]; expect(mortalTx.method.methodName).toEqual('transfer'); diff --git a/packages/types/src/interfaces/system/EventRecord.spec.ts b/packages/types/src/interfaces/system/EventRecord.spec.ts index 73207765a97a..9ec7328fc37c 100644 --- a/packages/types/src/interfaces/system/EventRecord.spec.ts +++ b/packages/types/src/interfaces/system/EventRecord.spec.ts @@ -2,30 +2,25 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { EventRecord } from './types'; - import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadataV0 from '@polkadot/metadata/Metadata/v0/static'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; -import { createType } from '../../codec/create'; -import Vec from '../../codec/Vec'; +import { createType, TypeRegistry } from '../../codec/create'; import json1 from '../../json/EventRecord.001.json'; import json3 from '../../json/EventRecord.003.json'; -import GenericEvent from '../../primitive/Generic/Event'; describe('EventRecord', (): void => { + const registry = new TypeRegistry(); + describe('EventRecord_0_76', (): void => { beforeEach((): void => { - GenericEvent.injectMetadata( - new Metadata(rpcMetadataV0) - ); + // eslint-disable-next-line no-new + new Metadata(registry, rpcMetadataV0); }); it('decodes correctly', (): void => { - const records: Vec = createType('Vec', json1.params.result.changes[0][1]) as any; + const records = createType(registry, 'Vec', json1.params.result.changes[0][1]) as any; const er = records[0]; expect(er.phase.type).toEqual('ApplyExtrinsic'); @@ -34,13 +29,12 @@ describe('EventRecord', (): void => { describe('EventRecord (current)', (): void => { beforeEach((): void => { - GenericEvent.injectMetadata( - new Metadata(rpcMetadata) - ); + // eslint-disable-next-line no-new + new Metadata(registry, rpcMetadata); }); it('decodes older eventrecord correctly', (): void => { - const records: Vec = createType('Vec', json1.params.result.changes[0][1], true) as any; + const records = createType(registry, 'Vec', json1.params.result.changes[0][1], true) as any; const er = records[0]; expect(er.phase.type).toEqual('ApplyExtrinsic'); @@ -48,7 +42,7 @@ describe('EventRecord', (): void => { it('decodes eventrecord with topics correctly', (): void => { const hex = json3.params.result.changes[0][1]; - const records: Vec = createType('Vec', hex, true) as any; + const records = createType(registry, 'Vec', hex, true) as any; const er = records[0]; expect(er.phase.type).toEqual('ApplyExtrinsic'); diff --git a/packages/types/src/primitive/Bool.spec.ts b/packages/types/src/primitive/Bool.spec.ts index e484a300c2c4..40fc118e4d83 100644 --- a/packages/types/src/primitive/Bool.spec.ts +++ b/packages/types/src/primitive/Bool.spec.ts @@ -2,18 +2,22 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import Bool from './Bool'; import { CodecTo } from '../types'; +import { TypeRegistry } from '../codec'; +import Bool from './Bool'; + describe('Bool', (): void => { + const registry = new TypeRegistry(); + describe('decode', (): void => { // eslint-disable-next-line @typescript-eslint/ban-types const testDecode = (type: string, input: Uint8Array | boolean | Boolean | Bool | number, expected: boolean): void => it(`can decode from ${type}`, (): void => { - expect(new Bool(input).toJSON()).toBe(expected); + expect(new Bool(registry, input).toJSON()).toBe(expected); }); - testDecode('Bool', new Bool(true), true); + testDecode('Bool', new Bool(registry, true), true); testDecode('Boolean', Boolean(true), true); testDecode('boolean', true, true); testDecode('number', 1, true); @@ -23,7 +27,7 @@ describe('Bool', (): void => { describe('encode', (): void => { const testEncode = (to: CodecTo, expected: string | Uint8Array | boolean, value: boolean): void => it(`can encode ${to}`, (): void => { - expect(new Bool(value)[to]()).toEqual(expected); + expect(new Bool(registry, value)[to]()).toEqual(expected); }); testEncode('toJSON', true, true); @@ -34,24 +38,24 @@ describe('Bool', (): void => { }); it('correctly encodes length', (): void => { - expect(new Bool(true).encodedLength).toEqual(1); + expect(new Bool(registry, true).encodedLength).toEqual(1); }); describe('utils', (): void => { it('compares agains a boolean', (): void => { - expect(new Bool(true).eq(true)).toBe(true); + expect(new Bool(registry, true).eq(true)).toBe(true); }); it('compares agains a Bool', (): void => { - expect(new Bool(false).eq(new Bool(false))).toBe(true); + expect(new Bool(registry, false).eq(new Bool(registry, false))).toBe(true); }); it('has isTrue', (): void => { - expect(new Bool(true).isTrue).toBe(true); + expect(new Bool(registry, true).isTrue).toBe(true); }); it('has isFalse', (): void => { - expect(new Bool(true).isFalse).toBe(false); + expect(new Bool(registry, true).isFalse).toBe(false); }); }); }); diff --git a/packages/types/src/primitive/Bool.ts b/packages/types/src/primitive/Bool.ts index 4e886fe44ea9..15d523f2a211 100644 --- a/packages/types/src/primitive/Bool.ts +++ b/packages/types/src/primitive/Bool.ts @@ -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 { Codec, IHash } from '../types'; +import { Codec, IHash, Registry } from '../types'; import { isU8a, u8aToHex } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -16,11 +16,13 @@ import { createType } from '../codec/create'; * @noInheritDoc */ export default class Bool extends Boolean implements Codec { + public readonly registry: Registry; + // eslint-disable-next-line @typescript-eslint/ban-types - constructor (value: Bool | Boolean | Uint8Array | boolean | number = false) { - super( - Bool.decodeBool(value) - ); + constructor (registry: Registry, value: Bool | Boolean | Uint8Array | boolean | number = false) { + super(Bool.decodeBool(value)); + + this.registry = registry; } private static decodeBool (value: any): boolean { @@ -44,7 +46,7 @@ export default class Bool extends Boolean implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return createType('Hash', blake2AsU8a(this.toU8a(), 256)); + return createType(this.registry, 'Hash', blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/primitive/Bytes.spec.ts b/packages/types/src/primitive/Bytes.spec.ts index 21b66fd343fd..371cfca54501 100644 --- a/packages/types/src/primitive/Bytes.spec.ts +++ b/packages/types/src/primitive/Bytes.spec.ts @@ -2,37 +2,37 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../injector'; - -import { createType } from '../codec/create'; +import { createType, TypeRegistry } from '../codec/create'; import Bytes from './Bytes'; describe('Bytes', (): void => { + const registry = new TypeRegistry(); const CODE = new Uint8Array([0x14, 0x3a, 0x63, 0x6f, 0x64, 0x65]); describe('construction', (): void => { it('decodes when input is string', (): void => { expect( - new Bytes(':code').toU8a() + new Bytes(registry, ':code').toU8a() ).toEqual(CODE); }); it('decodes when hex is not length prefixed', (): void => { expect( - new Bytes('0x3a636f6465').toU8a() + new Bytes(registry, '0x3a636f6465').toU8a() ).toEqual(CODE); }); it('decodes from number[]', (): void => { expect( - new Bytes([0x3a, 0x63, 0x6f, 0x64, 0x65]).toU8a() + new Bytes(registry, [0x3a, 0x63, 0x6f, 0x64, 0x65]).toU8a() ).toEqual(CODE); }); it('creates via storagedata (no prefix)', (): void => { expect( new Bytes( - createType('StorageData', '0x3a636f6465') + registry, + createType(registry, 'StorageData', '0x3a636f6465') ).toU8a() ).toEqual(CODE); }); diff --git a/packages/types/src/primitive/Bytes.ts b/packages/types/src/primitive/Bytes.ts index f176e3b41147..272d3ee9d2fe 100644 --- a/packages/types/src/primitive/Bytes.ts +++ b/packages/types/src/primitive/Bytes.ts @@ -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 { AnyU8a } from '../types'; +import { AnyU8a, Registry } from '../types'; import { assert, isString, isU8a, u8aToU8a } from '@polkadot/util'; @@ -17,8 +17,8 @@ import U8a from '../codec/U8a'; * as what is found in [[Text]] and [[Vec]]) */ export default class Bytes extends U8a { - constructor (value?: AnyU8a) { - super(Bytes.decodeBytes(value)); + constructor (registry: Registry, value?: AnyU8a) { + super(registry, Bytes.decodeBytes(value)); } private static decodeBytes (value?: AnyU8a): Uint8Array | undefined { diff --git a/packages/types/src/primitive/Extrinsic/Extrinsic.spec.ts b/packages/types/src/primitive/Extrinsic/Extrinsic.spec.ts index 718e49290b88..84fe0d8c280e 100644 --- a/packages/types/src/primitive/Extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/primitive/Extrinsic/Extrinsic.spec.ts @@ -2,23 +2,23 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import { hexToU8a } from '@polkadot/util'; -import Call from '../Generic/Call'; +import { TypeRegistry } from '../../codec'; import Extrinsic from './Extrinsic'; -describe('Extrinsic', (): void => { - beforeAll((): void => { - Call.injectMetadata(new Metadata(rpcMetadata)); - }); +const registry = new TypeRegistry(); +// eslint-disable-next-line no-new +new Metadata(registry, rpcMetadata); + +describe('Extrinsic', (): void => { describe('V1', (): void => { it('decodes an actual transaction (length prefix)', (): void => { const extrinsic = new Extrinsic( + registry, '0x' + '2502' + '81' + @@ -46,6 +46,7 @@ describe('Extrinsic', (): void => { it('decodes an actual transaction (no length prefix, old version)', (): void => { const extrinsic = new Extrinsic( + registry, '0x' + '81' + 'ff' + @@ -74,6 +75,7 @@ describe('Extrinsic', (): void => { describe('V2', (): void => { it('decodes an actual transaction (length prefix)', (): void => { const extrinsic = new Extrinsic( + registry, '0x' + '2902' + // yes, longer than in v1, 1 byte for the tip '82' + @@ -118,7 +120,7 @@ describe('Extrinsic', (): void => { 'e5c0'; expect( - new Extrinsic(new Extrinsic(input)).toU8a() + new Extrinsic(registry, new Extrinsic(registry, input)).toU8a() ).toEqual(hexToU8a(input)); }); }); diff --git a/packages/types/src/primitive/Extrinsic/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/Extrinsic.ts index d79c450b0626..11ad0089c9ad 100644 --- a/packages/types/src/primitive/Extrinsic/Extrinsic.ts +++ b/packages/types/src/primitive/Extrinsic/Extrinsic.ts @@ -4,7 +4,7 @@ import { FunctionMetadataLatest } from '../../interfaces/metadata/types'; import { Address, Balance, Call, EcdsaSignature, Ed25519Signature, ExtrinsicUnknown, ExtrinsicV1, ExtrinsicV2, ExtrinsicV3, ExtrinsicV4, Index, Sr25519Signature } from '../../interfaces/runtime'; -import { AnyU8a, ArgsDef, Codec, ExtrinsicPayloadValue, IExtrinsic, IKeyringPair, InterfaceTypes, SignatureOptions } from '../../types'; +import { AnyU8a, ArgsDef, Codec, ExtrinsicPayloadValue, IExtrinsic, IKeyringPair, InterfaceTypes, Registry, SignatureOptions } from '../../types'; import { assert, isHex, isU8a, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; @@ -51,11 +51,11 @@ export { TRANSACTION_VERSION as LATEST_EXTRINSIC_VERSION } from './v4/Extrinsic' * - left as is, to create an inherent */ export default class Extrinsic extends Base implements IExtrinsic { - constructor (value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, { version }: CreateOptions = {}) { - super(Extrinsic.decodeExtrinsic(value, version)); + constructor (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, { version }: CreateOptions = {}) { + super(registry, Extrinsic.decodeExtrinsic(registry, value, version)); } - private static newFromValue (value: any, version: number): ExtrinsicVx | ExtrinsicUnknown { + private static newFromValue (registry: Registry, value: any, version: number): ExtrinsicVx | ExtrinsicUnknown { if (value instanceof Extrinsic) { return value.raw; } @@ -65,22 +65,22 @@ export default class Extrinsic extends Base impl // we cast here since the VERSION definition is incredibly broad - we don't have a // slice for "only add extrinsic types", and more string definitions become unwieldy - return createType(type, value, { isSigned, version }) as ExtrinsicVx; + return createType(registry, type, value, { isSigned, version }) as ExtrinsicVx; } - public static decodeExtrinsic (value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, version: number = DEFAULT_VERSION): ExtrinsicVx | ExtrinsicUnknown { + public static decodeExtrinsic (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, version: number = DEFAULT_VERSION): ExtrinsicVx | ExtrinsicUnknown { if (Array.isArray(value) || isHex(value)) { - return Extrinsic.decodeU8aLike(value, version); + return Extrinsic.decodeU8aLike(registry, value, version); } else if (isU8a(value)) { - return Extrinsic.decodeU8a(value, version); - } else if (value instanceof ClassOf('Call')) { - return Extrinsic.newFromValue({ method: value }, version); + return Extrinsic.decodeU8a(registry, value, version); + } else if (value instanceof ClassOf(registry, 'Call')) { + return Extrinsic.newFromValue(registry, { method: value }, version); } - return Extrinsic.newFromValue(value, version); + return Extrinsic.newFromValue(registry, value, version); } - private static decodeU8aLike (value: string | number[], version: number): ExtrinsicVx | ExtrinsicUnknown { + private static decodeU8aLike (registry: Registry, value: string | number[], version: number): ExtrinsicVx | ExtrinsicUnknown { // Instead of the block below, it should simply be: // return Extrinsic.decodeExtrinsic(hexToU8a(value as string)); const u8a = u8aToU8a(value); @@ -92,6 +92,7 @@ export default class Extrinsic extends Base impl const withPrefix = u8a.length === (offset + length.toNumber()); return Extrinsic.decodeU8a( + registry, withPrefix ? u8a : Compact.addLengthPrefix(u8a), @@ -99,9 +100,9 @@ export default class Extrinsic extends Base impl ); } - private static decodeU8a (value: Uint8Array, version: number): ExtrinsicVx | ExtrinsicUnknown { + private static decodeU8a (registry: Registry, value: Uint8Array, version: number): ExtrinsicVx | ExtrinsicUnknown { if (!value.length) { - return Extrinsic.newFromValue(new Uint8Array(), version); + return Extrinsic.newFromValue(registry, new Uint8Array(), version); } const [offset, length] = Compact.decodeU8a(value); @@ -111,7 +112,7 @@ export default class Extrinsic extends Base impl const data = value.subarray(offset, total); - return Extrinsic.newFromValue(data.subarray(1), data[0]); + return Extrinsic.newFromValue(registry, data.subarray(1), data[0]); } /** diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicEra.spec.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicEra.spec.ts index b76b720af773..b6e46efbb000 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicEra.spec.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicEra.spec.ts @@ -2,46 +2,47 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - +import { TypeRegistry } from '../../codec'; import ExtrinsicEra from './ExtrinsicEra'; describe('ExtrinsicEra', (): void => { + const registry = new TypeRegistry(); + it('decodes an Extrinsic Era with immortal', (): void => { - const extrinsicEra = new ExtrinsicEra(new Uint8Array([0])); + const extrinsicEra = new ExtrinsicEra(registry, new Uint8Array([0])); expect(extrinsicEra.asImmortalEra).toBeDefined(); expect(extrinsicEra.toJSON()).toEqual({ ImmortalEra: '0x00' }); }); it('decodes an Extrinsic Era from u8 as mortal', (): void => { - const extrinsicEra = new ExtrinsicEra(new Uint8Array([78, 156])); + const extrinsicEra = new ExtrinsicEra(registry, new Uint8Array([78, 156])); expect(extrinsicEra.asMortalEra.period.toNumber()).toEqual(32768); expect(extrinsicEra.asMortalEra.phase.toNumber()).toEqual(20000); }); it('decoded from an existing ExtrinsicEra', (): void => { - const extrinsicEra = new ExtrinsicEra(new ExtrinsicEra(new Uint8Array([78, 156]))); + const extrinsicEra = new ExtrinsicEra(registry, new ExtrinsicEra(registry, new Uint8Array([78, 156]))); expect(extrinsicEra.asMortalEra.period.toNumber()).toEqual(32768); expect(extrinsicEra.asMortalEra.phase.toNumber()).toEqual(20000); }); it('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', (): void => { - const extrinsicEra = new ExtrinsicEra({ current: 1400, period: 200 }); + const extrinsicEra = new ExtrinsicEra(registry, { current: 1400, period: 200 }); expect(extrinsicEra.asMortalEra.period.toNumber()).toEqual(256); expect(extrinsicEra.asMortalEra.phase.toNumber()).toEqual(120); }); it('serializes and de-serializes from JSON', (): void => { - const extrinsicEra = new ExtrinsicEra(new Uint8Array([78, 156])); + const extrinsicEra = new ExtrinsicEra(registry, new Uint8Array([78, 156])); const u8a = extrinsicEra.toU8a(); const json = extrinsicEra.toJSON(); expect(u8a).toEqual(new Uint8Array([78, 156])); expect(json).toEqual({ MortalEra: '0x4e9c' }); - expect(new ExtrinsicEra(json).toU8a()).toEqual(u8a); + expect(new ExtrinsicEra(registry, json).toU8a()).toEqual(u8a); }); }); diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicEra.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicEra.ts index 43d3bf60c353..b77e4ad6852a 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicEra.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicEra.ts @@ -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 { AnyU8a, IExtrinsicEra } from '../../types'; +import { AnyU8a, IExtrinsicEra, Registry } from '../../types'; import BN from 'bn.js'; import { assert, bnToBn, hexToU8a, isHex, isU8a, isObject, u8aToBn } from '@polkadot/util'; @@ -35,10 +35,10 @@ interface ImmortalEnumDef { */ export class ImmortalEra extends U8a { // eslint-disable-next-line @typescript-eslint/no-unused-vars - constructor (value?: AnyU8a) { + constructor (registry: Registry, value?: AnyU8a) { // For immortals, we always provide the known value (i.e. treated as a // constant no matter how it is constructed - it is a fixed structure) - super(IMMORTAL_ERA); + super(registry, IMMORTAL_ERA); } } @@ -48,30 +48,30 @@ export class ImmortalEra extends U8a { * The MortalEra for an extrinsic, indicating period and phase */ export class MortalEra extends Tuple { - constructor (value?: MortalMethod | Uint8Array | number[] | string) { - super({ + constructor (registry: Registry, value?: MortalMethod | Uint8Array | number[] | string) { + super(registry, { period: U64, phase: U64 - }, MortalEra.decodeMortalEra(value)); + }, MortalEra.decodeMortalEra(registry, value)); } - private static decodeMortalEra (value?: MortalMethod | Uint8Array | number[] | string): MortalEraValue { + private static decodeMortalEra (registry: Registry, value?: MortalMethod | Uint8Array | number[] | string): MortalEraValue { if (isHex(value)) { - return MortalEra.decodeMortalU8a(hexToU8a(value)); + return MortalEra.decodeMortalU8a(registry, hexToU8a(value)); } else if (Array.isArray(value)) { - return MortalEra.decodeMortalU8a(new Uint8Array(value)); + return MortalEra.decodeMortalU8a(registry, new Uint8Array(value)); } else if (isU8a(value)) { - return MortalEra.decodeMortalU8a(value); + return MortalEra.decodeMortalU8a(registry, value); } else if (isObject(value)) { - return MortalEra.decodeMortalObject(value); + return MortalEra.decodeMortalObject(registry, value); } else if (!value) { - return [new U64(), new U64()]; + return [new U64(registry), new U64(registry)]; } throw new Error('Invalid data passed to Mortal era'); } - private static decodeMortalObject (value: MortalMethod): MortalEraValue { + private static decodeMortalObject (registry: Registry, value: MortalMethod): MortalEraValue { const { current, period } = value; let calPeriod = Math.pow(2, Math.ceil(Math.log2(period))); calPeriod = Math.min(Math.max(calPeriod, 4), 1 << 16); @@ -79,12 +79,12 @@ export class MortalEra extends Tuple { const quantizeFactor = Math.max(calPeriod >> 12, 1); const quantizedPhase = phase / quantizeFactor * quantizeFactor; - return [new U64(calPeriod), new U64(quantizedPhase)]; + return [new U64(registry, calPeriod), new U64(registry, quantizedPhase)]; } - private static decodeMortalU8a (value: Uint8Array): MortalEraValue { + private static decodeMortalU8a (registry: Registry, value: Uint8Array): MortalEraValue { if (value.length === 0) { - return [new U64(), new U64()]; + return [new U64(registry), new U64(registry)]; } const first = u8aToBn(value.subarray(0, 1)).toNumber(); @@ -96,7 +96,7 @@ export class MortalEra extends Tuple { assert(period >= 4 && phase < period, 'Invalid data passed to Mortal era'); - return [new U64(period), new U64(phase)]; + return [new U64(registry, period), new U64(registry, phase)]; } /** @@ -191,8 +191,8 @@ export class MortalEra extends Tuple { * The era for an extrinsic, indicating either a mortal or immortal extrinsic */ export default class ExtrinsicEra extends Enum implements IExtrinsicEra { - constructor (value?: any) { - super({ + constructor (registry: Registry, value?: any) { + super(registry, { ImmortalEra, MortalEra }, ExtrinsicEra.decodeExtrinsicEra(value)); diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.spec.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.spec.ts index b45a979a3198..9b95902d1c4e 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.spec.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.spec.ts @@ -2,11 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - +import { TypeRegistry } from '../../codec'; import ExtrinsicPayload from './ExtrinsicPayload'; describe('ExtrinsicPayload', (): void => { + const registry = new TypeRegistry(); const TEST = { address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE', blockHash: '0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7', @@ -19,15 +19,15 @@ describe('ExtrinsicPayload', (): void => { }; it('creates and can re-create from itself (U8a)', (): void => { - const a = new ExtrinsicPayload(TEST, { version: 3 }); - const b = new ExtrinsicPayload(a.toU8a(), { version: 3 }); + const a = new ExtrinsicPayload(registry, TEST, { version: 3 }); + const b = new ExtrinsicPayload(registry, a.toU8a(), { version: 3 }); expect(a).toEqual(b); }); it('creates and can re-create from itself (hex)', (): void => { - const a = new ExtrinsicPayload(TEST, { version: 3 }); - const b = new ExtrinsicPayload(a.toHex(), { version: 3 }); + const a = new ExtrinsicPayload(registry, TEST, { version: 3 }); + const b = new ExtrinsicPayload(registry, a.toHex(), { version: 3 }); expect(a).toEqual(b); }); diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts index 288cd8b61a50..1460425a03a7 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Balance, ExtrinsicPayloadV1, ExtrinsicPayloadV2, ExtrinsicPayloadV3, ExtrinsicPayloadV4, Hash, Index } from '../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes } from '../../types'; +import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes, Registry } from '../../types'; import { u8aToHex } from '@polkadot/util'; @@ -37,18 +37,16 @@ const VERSIONS: InterfaceTypes[] = [ * on the contents included */ export default class ExtrinsicPayload extends Base { - constructor (value: Partial | Uint8Array | string | undefined, { version }: ExtrinsicPayloadOptions = {}) { - super( - ExtrinsicPayload.decodeExtrinsicPayload(value as ExtrinsicPayloadValue, version) - ); + constructor (registry: Registry, value: Partial | Uint8Array | string | undefined, { version }: ExtrinsicPayloadOptions = {}) { + super(registry, ExtrinsicPayload.decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version)); } - public static decodeExtrinsicPayload (value: ExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string | undefined, version: number = DEFAULT_VERSION): ExtrinsicPayloadVx { + public static decodeExtrinsicPayload (registry: Registry, value: ExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string | undefined, version: number = DEFAULT_VERSION): ExtrinsicPayloadVx { if (value instanceof ExtrinsicPayload) { return value.raw; } - return createType(VERSIONS[version] || VERSIONS[0], value, { version }) as ExtrinsicPayloadVx; + return createType(registry, VERSIONS[version] || VERSIONS[0], value, { version }) as ExtrinsicPayloadVx; } /** @@ -70,7 +68,7 @@ export default class ExtrinsicPayload extends Base { */ public get genesisHash (): Hash { // NOTE only v3+ - return (this.raw as ExtrinsicPayloadV3).genesisHash || createType('Hash'); + return (this.raw as ExtrinsicPayloadV3).genesisHash || createType(this.registry, 'Hash'); } /** @@ -92,7 +90,7 @@ export default class ExtrinsicPayload extends Base { */ public get specVersion (): u32 { // NOTE only v3+ - return (this.raw as ExtrinsicPayloadV3).specVersion || createType('u32'); + return (this.raw as ExtrinsicPayloadV3).specVersion || createType(this.registry, 'u32'); } /** @@ -100,7 +98,7 @@ export default class ExtrinsicPayload extends Base { */ public get tip (): Compact { // NOTE from v2+ - return (this.raw as ExtrinsicPayloadV2).tip || createType('Compact'); + return (this.raw as ExtrinsicPayloadV2).tip || createType(this.registry, 'Compact'); } /** diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicPayloadUnknown.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicPayloadUnknown.ts index 79227b4faedb..81524a8391d5 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicPayloadUnknown.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicPayloadUnknown.ts @@ -2,6 +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 { Registry } from '../../types'; import { ExtrinsicPayloadOptions } from './types'; import Struct from '../../codec/Struct'; @@ -12,8 +13,8 @@ import Struct from '../../codec/Struct'; * A default handler for payloads where the version is not known (default throw) */ export default class ExtrinsicPayloadUnknown extends Struct { - constructor (value?: any, { version = 0 }: Partial = {}) { - super({}); + constructor (registry: Registry, value?: any, { version = 0 }: Partial = {}) { + super(registry, {}); throw new Error(`Unsupported extrinsic payload version ${version}`); } diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicUnknown.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicUnknown.ts index c138795038c3..f751231af6c4 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicUnknown.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicUnknown.ts @@ -2,6 +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 { Registry } from '../../types'; import { ExtrinsicOptions } from './types'; import Struct from '../../codec/Struct'; @@ -13,8 +14,8 @@ import { UNMASK_VERSION } from './constants'; * A default handler for extrinsics where the version is not known (default throw) */ export default class ExtrinsicUnknown extends Struct { - constructor (value?: any, { isSigned = false, version = 0 }: Partial = {}) { - super({}); + constructor (registry: Registry, value?: any, { isSigned = false, version = 0 }: Partial = {}) { + super(registry, {}); throw new Error(`Unsupported ${isSigned ? '' : 'un'}signed extrinsic version ${version & UNMASK_VERSION}`); } diff --git a/packages/types/src/primitive/Extrinsic/SignerPayload.spec.ts b/packages/types/src/primitive/Extrinsic/SignerPayload.spec.ts index 7bddbb2450ca..25bb2266f297 100644 --- a/packages/types/src/primitive/Extrinsic/SignerPayload.spec.ts +++ b/packages/types/src/primitive/Extrinsic/SignerPayload.spec.ts @@ -2,12 +2,19 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { createType, GenericCall } from '@polkadot/types'; +import { createType } from '@polkadot/types'; import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; + +import { TypeRegistry } from '../../codec'; import SignerPayload from './SignerPayload'; +const registry = new TypeRegistry(); + +// eslint-disable-next-line no-new +new Metadata(registry, rpcMetadata); + describe('SignerPayload', (): void => { const TEST = { address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE', @@ -22,19 +29,15 @@ describe('SignerPayload', (): void => { version: 3 }; - beforeEach((): void => { - GenericCall.injectMetadata(new Metadata(rpcMetadata)); - }); - it('creates a valid JSON output', (): void => { expect( - new SignerPayload({ + new SignerPayload(registry, { address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE', blockHash: '0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7', blockNumber: '0x231d30', - era: createType('ExtrinsicEra', { current: 2301232, period: 200 }), + era: createType(registry, 'ExtrinsicEra', { current: 2301232, period: 200 }), genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b', - method: createType('Call', '0x0600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c'), + method: createType(registry, 'Call', '0x0600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c'), nonce: 0x1234, tip: 0x5678, version: 3 @@ -55,21 +58,22 @@ describe('SignerPayload', (): void => { it('re-constructs from JSON', (): void => { expect( - new SignerPayload(TEST).toPayload() + new SignerPayload(registry, TEST).toPayload() ).toEqual(TEST); }); it('re-constructs from itself', (): void => { expect( new SignerPayload( - new SignerPayload(TEST) + registry, + new SignerPayload(registry, TEST) ).toPayload() ).toEqual(TEST); }); it('can be used as a feed to ExtrinsicPayload', (): void => { - const signer = new SignerPayload(TEST).toPayload(); - const payload = createType('ExtrinsicPayload', signer, { version: signer.version }); + const signer = new SignerPayload(registry, TEST).toPayload(); + const payload = createType(registry, 'ExtrinsicPayload', signer, { version: signer.version }); expect(payload.era.toHex()).toEqual(TEST.era); expect(payload.method.toHex()).toEqual(TEST.method); diff --git a/packages/types/src/primitive/Extrinsic/SignerPayload.ts b/packages/types/src/primitive/Extrinsic/SignerPayload.ts index 5c1eea2243f7..78457c860a67 100644 --- a/packages/types/src/primitive/Extrinsic/SignerPayload.ts +++ b/packages/types/src/primitive/Extrinsic/SignerPayload.ts @@ -65,7 +65,7 @@ export default class SignerPayload extends _Payload implements ISignerPayload { public toRaw (): SignerPayloadRaw { const payload = this.toPayload(); // NOTE Explicitly pass the bare flag so the method is encoded un-prefixed (non-decodable, for signing only) - const data = u8aToHex(createType('ExtrinsicPayload', payload, { version: payload.version }).toU8a(true)); + const data = u8aToHex(createType(this.registry, 'ExtrinsicPayload', payload, { version: payload.version }).toU8a(true)); return { address: payload.address, diff --git a/packages/types/src/primitive/Extrinsic/v1/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/v1/Extrinsic.ts index f95d96917d92..895952ede314 100644 --- a/packages/types/src/primitive/Extrinsic/v1/Extrinsic.ts +++ b/packages/types/src/primitive/Extrinsic/v1/Extrinsic.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Call } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicOptions } from '../types'; import { isU8a } from '@polkadot/util'; @@ -25,22 +25,22 @@ const TRANSACTION_VERSION = 1; * The first generation of compact extrinsics */ export default class ExtrinsicV1 extends Struct implements IExtrinsicImpl { - constructor (value?: Uint8Array | ExtrinsicValueV1, { isSigned }: Partial = {}) { - super({ + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV1, { isSigned }: Partial = {}) { + super(registry, { signature: ExtrinsicSignatureV1, method: 'Call' - }, ExtrinsicV1.decodeExtrinsic(value, isSigned)); + }, ExtrinsicV1.decodeExtrinsic(registry, value, isSigned)); } - public static decodeExtrinsic (value?: Uint8Array | ExtrinsicValueV1, isSigned = false): ExtrinsicValueV1 { + public static decodeExtrinsic (registry: Registry, value?: Uint8Array | ExtrinsicValueV1, isSigned = false): ExtrinsicValueV1 { if (!value) { return {}; } else if (value instanceof ExtrinsicV1) { return value; } else if (isU8a(value)) { // here we decode manually since we need to pull through the version information - const signature = new ExtrinsicSignatureV1(value, { isSigned }); - const method = createType('Call', value.subarray(signature.encodedLength)); + const signature = new ExtrinsicSignatureV1(registry, value, { isSigned }); + const method = createType(registry, 'Call', value.subarray(signature.encodedLength)); return { method, diff --git a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts index 4348799ed775..992a11b5b72c 100644 --- a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Hash, ExtrinsicEra, Index } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IKeyringPair } from '../../../types'; +import { ExtrinsicPayloadValue, IKeyringPair, Registry } from '../../../types'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; @@ -22,8 +22,8 @@ import { sign } from '../util'; * 32 bytes: The hash of the authoring block implied by the Transaction Era and the current block. */ export default class ExtrinsicPayloadV1 extends Struct { - constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { - super({ + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | string) { + super(registry, { nonce: 'Compact', method: 'Bytes', era: 'ExtrinsicEra', diff --git a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts index 09f1565314d2..0345bcdea66b 100644 --- a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Balance, Call, ExtrinsicEra, Index, Signature } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicSignatureOptions } from '../types'; import { createType } from '../../../codec/create'; @@ -23,8 +23,8 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi // 64 bytes: The sr25519/ed25519 signature of the Signing Payload // 1-8 bytes: The Compact of the signing account // 1/2 bytes: The Transaction Era - constructor (value?: ExtrinsicSignatureV1 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { - super({ + constructor (registry: Registry, value?: ExtrinsicSignatureV1 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { + super(registry, { signer: 'Address', signature: 'Signature', nonce: 'Compact', @@ -92,7 +92,7 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi * @description Forwards compat */ public get tip (): Compact { - return createType('Compact', 0); + return createType(this.registry, 'Compact', 0); } private injectSignature (signer: Address, signature: Signature, { era, nonce }: ExtrinsicPayloadV1): IExtrinsicSignature { @@ -109,9 +109,9 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi */ public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { return this.injectSignature( - createType('Address', signer), - createType('Signature', signature), - new ExtrinsicPayloadV1(payload) + createType(this.registry, 'Address', signer), + createType(this.registry, 'Signature', signature), + new ExtrinsicPayloadV1(this.registry, payload) ); } @@ -119,8 +119,8 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi * @description Generate a payload and applies the signature from a keypair */ public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce }: SignatureOptions): IExtrinsicSignature { - const signer = createType('Address', account.publicKey); - const payload = new ExtrinsicPayloadV1({ + const signer = createType(this.registry, 'Address', account.publicKey); + const payload = new ExtrinsicPayloadV1(this.registry, { blockHash, era: era || IMMORTAL_ERA, genesisHash, @@ -129,7 +129,7 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi specVersion: 0, // unused for v1, added for compat tip: 0 }); - const signature = createType('Signature', payload.sign(account)); + const signature = createType(this.registry, 'Signature', payload.sign(account)); return this.injectSignature(signer, signature, payload); } diff --git a/packages/types/src/primitive/Extrinsic/v2/Extrinsic.spec.ts b/packages/types/src/primitive/Extrinsic/v2/Extrinsic.spec.ts index c0bccb472c38..0bc019e04697 100644 --- a/packages/types/src/primitive/Extrinsic/v2/Extrinsic.spec.ts +++ b/packages/types/src/primitive/Extrinsic/v2/Extrinsic.spec.ts @@ -2,34 +2,33 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../../injector'; - import BN from 'bn.js'; import Decorated from '@polkadot/metadata/Decorated'; import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import testingPairs from '@polkadot/keyring/testingPairs'; -import Call from '../../Generic/Call'; +import { TypeRegistry } from '../../../codec'; import Extrinsic from './Extrinsic'; -const decorated = new Decorated(rpcMetadata); +const registry = new TypeRegistry(); +const decorated = new Decorated(registry, rpcMetadata); const keyring = testingPairs({ type: 'ed25519' }, false); -describe('ExtrinsicV2', (): void => { - beforeEach((): void => { - Call.injectMetadata(new Metadata(rpcMetadata)); - }); +// eslint-disable-next-line no-new +new Metadata(registry, rpcMetadata); +describe('ExtrinsicV2', (): void => { it('constructs a sane Uint8Array (default)', (): void => { expect( - new Extrinsic().toU8a() + new Extrinsic(registry).toU8a() ).toEqual(new Uint8Array([0, 0])); }); it('creates a unsigned extrinsic', (): void => { expect( new Extrinsic( + registry, decorated.tx.balances.transfer(keyring.bob.publicKey, 6969) ).toHex() ).toEqual( @@ -44,6 +43,7 @@ describe('ExtrinsicV2', (): void => { it('creates a signed extrinsic', (): void => { expect( new Extrinsic( + registry, decorated.tx.balances.transfer(keyring.bob.publicKey, 6969) ).sign(keyring.alice, { blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', diff --git a/packages/types/src/primitive/Extrinsic/v2/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/v2/Extrinsic.ts index 854978a0a5bb..4b48b4c8122b 100644 --- a/packages/types/src/primitive/Extrinsic/v2/Extrinsic.ts +++ b/packages/types/src/primitive/Extrinsic/v2/Extrinsic.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Call } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicOptions } from '../types'; import { isU8a } from '@polkadot/util'; @@ -25,24 +25,24 @@ export interface ExtrinsicValueV2 { * The second generation of compact extrinsics */ export default class ExtrinsicV2 extends Struct implements IExtrinsicImpl { - constructor (value?: Uint8Array | ExtrinsicValueV2 | Call, { isSigned }: Partial = {}) { - super({ + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV2 | Call, { isSigned }: Partial = {}) { + super(registry, { signature: ExtrinsicSignatureV2, method: 'Call' - }, ExtrinsicV2.decodeExtrinsic(value, isSigned)); + }, ExtrinsicV2.decodeExtrinsic(registry, value, isSigned)); } - public static decodeExtrinsic (value?: Call | Uint8Array | ExtrinsicValueV2, isSigned = false): ExtrinsicValueV2 { + public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV2, isSigned = false): ExtrinsicValueV2 { if (!value) { return {}; } else if (value instanceof ExtrinsicV2) { return value; - } else if (value instanceof ClassOf('Call')) { + } else if (value instanceof ClassOf(registry, 'Call')) { return { method: value }; } else if (isU8a(value)) { // here we decode manually since we need to pull through the version information - const signature = new ExtrinsicSignatureV2(value, { isSigned }); - const method = createType('Call', value.subarray(signature.encodedLength)); + const signature = new ExtrinsicSignatureV2(registry, value, { isSigned }); + const method = createType(registry, 'Call', value.subarray(signature.encodedLength)); return { method, diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts index c7f720206a5a..a53ddde5e8c4 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Balance, ExtrinsicEra, Hash, Index } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes } from '../../../types'; +import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes, Registry } from '../../../types'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; @@ -40,8 +40,8 @@ const SignedPayloadDefV2: Record = { * on the contents included */ export default class ExtrinsicPayloadV2 extends Struct { - constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { - super(SignedPayloadDefV2, value); + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | string) { + super(registry, SignedPayloadDefV2, value); } /** diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.spec.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.spec.ts index dc29ee1bdb05..24db34ae56bd 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.spec.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.spec.ts @@ -2,11 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../../injector'; - +import { TypeRegistry } from '../../../codec'; import ExtrinsicSignature from './ExtrinsicSignature'; describe('ExtrinsicSignatureV2', (): void => { + const registry = new TypeRegistry(); + it('encodes to a sane Uint8Array', (): void => { const u8a = new Uint8Array([ // signer as an AccountIndex @@ -23,7 +24,7 @@ describe('ExtrinsicSignatureV2', (): void => { ]); expect( - new ExtrinsicSignature(u8a, { isSigned: true }).toU8a() + new ExtrinsicSignature(registry, u8a, { isSigned: true }).toU8a() ).toEqual(u8a); }); }); diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts index 0bb5ddd0a560..f05a10cd66fd 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Balance, Call, ExtrinsicEra, Index, Signature } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicSignatureOptions } from '../types'; import { createType } from '../../../codec/create'; @@ -18,8 +18,8 @@ import ExtrinsicPayloadV2 from './ExtrinsicPayload'; * A container for the [[Signature]] associated with a specific [[Extrinsic]] */ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSignature { - constructor (value: ExtrinsicSignatureV2 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { - super({ + constructor (registry: Registry, value: ExtrinsicSignatureV2 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { + super(registry, { signer: 'Address', signature: 'Signature', era: 'ExtrinsicEra', @@ -106,9 +106,9 @@ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSi */ public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { return this.injectSignature( - createType('Address', signer), - createType('Signature', signature), - new ExtrinsicPayloadV2(payload) + createType(this.registry, 'Address', signer), + createType(this.registry, 'Signature', signature), + new ExtrinsicPayloadV2(this.registry, payload) ); } @@ -116,8 +116,8 @@ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSi * @description Generate a payload and applies the signature from a keypair */ public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, tip }: SignatureOptions): IExtrinsicSignature { - const signer = createType('Address', account.publicKey); - const payload = new ExtrinsicPayloadV2({ + const signer = createType(this.registry, 'Address', account.publicKey); + const payload = new ExtrinsicPayloadV2(this.registry, { blockHash, era: era || IMMORTAL_ERA, genesisHash, @@ -126,7 +126,7 @@ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSi specVersion: 0, // unused for v2 tip: tip || 0 }); - const signature = createType('Signature', payload.sign(account)); + const signature = createType(this.registry, 'Signature', payload.sign(account)); return this.injectSignature(signer, signature, payload); } diff --git a/packages/types/src/primitive/Extrinsic/v3/Extrinsic.spec.ts b/packages/types/src/primitive/Extrinsic/v3/Extrinsic.spec.ts index bec887c70e93..ec6cb34c73b7 100644 --- a/packages/types/src/primitive/Extrinsic/v3/Extrinsic.spec.ts +++ b/packages/types/src/primitive/Extrinsic/v3/Extrinsic.spec.ts @@ -2,34 +2,33 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../../injector'; - import BN from 'bn.js'; import Decorated from '@polkadot/metadata/Decorated'; import Metadata from '@polkadot/metadata/Metadata'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import testingPairs from '@polkadot/keyring/testingPairs'; -import Call from '../../Generic/Call'; +import { TypeRegistry } from '../../../codec'; import Extrinsic from './Extrinsic'; -const decorated = new Decorated(rpcMetadata); +const registry = new TypeRegistry(); +const decorated = new Decorated(registry, rpcMetadata); const keyring = testingPairs({ type: 'ed25519' }, false); -describe('ExtrinsicV3', (): void => { - beforeEach((): void => { - Call.injectMetadata(new Metadata(rpcMetadata)); - }); +// eslint-disable-next-line no-new +new Metadata(registry, rpcMetadata); +describe('ExtrinsicV3', (): void => { it('constructs a sane Uint8Array (default)', (): void => { expect( - new Extrinsic().toU8a() + new Extrinsic(registry).toU8a() ).toEqual(new Uint8Array([0, 0])); }); it('creates a unsigned extrinsic', (): void => { expect( new Extrinsic( + registry, decorated.tx.balances.transfer(keyring.bob.publicKey, 6969) ).toHex() ).toEqual( @@ -44,6 +43,7 @@ describe('ExtrinsicV3', (): void => { it('creates a signed extrinsic', (): void => { expect( new Extrinsic( + registry, decorated.tx.balances.transfer(keyring.bob.publicKey, 6969) ).sign(keyring.alice, { blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', diff --git a/packages/types/src/primitive/Extrinsic/v3/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/v3/Extrinsic.ts index 16492d581504..f49af88a7f59 100644 --- a/packages/types/src/primitive/Extrinsic/v3/Extrinsic.ts +++ b/packages/types/src/primitive/Extrinsic/v3/Extrinsic.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Call } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicOptions } from '../types'; import { isU8a } from '@polkadot/util'; @@ -25,24 +25,24 @@ export interface ExtrinsicValueV3 { * The third generation of compact extrinsics */ export default class ExtrinsicV3 extends Struct implements IExtrinsicImpl { - constructor (value?: Uint8Array | ExtrinsicValueV3 | Call, { isSigned }: Partial = {}) { - super({ + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV3 | Call, { isSigned }: Partial = {}) { + super(registry, { signature: ExtrinsicSignatureV3, method: 'Call' - }, ExtrinsicV3.decodeExtrinsic(value, isSigned)); + }, ExtrinsicV3.decodeExtrinsic(registry, value, isSigned)); } - public static decodeExtrinsic (value?: Call | Uint8Array | ExtrinsicValueV3, isSigned = false): ExtrinsicValueV3 { + public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV3, isSigned = false): ExtrinsicValueV3 { if (!value) { return {}; } else if (value instanceof ExtrinsicV3) { return value; - } else if (value instanceof ClassOf('Call')) { + } else if (value instanceof ClassOf(registry, 'Call')) { return { method: value }; } else if (isU8a(value)) { // here we decode manually since we need to pull through the version information - const signature = new ExtrinsicSignatureV3(value, { isSigned }); - const method = createType('Call', value.subarray(signature.encodedLength)); + const signature = new ExtrinsicSignatureV3(registry, value, { isSigned }); + const method = createType(registry, 'Call', value.subarray(signature.encodedLength)); return { method, diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts index 720db4625441..e4c3cbbd0425 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Balance, ExtrinsicEra, Hash, Index } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes } from '../../../types'; +import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes, Registry } from '../../../types'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; @@ -38,8 +38,8 @@ export const SignedPayloadDefV3: Record = { * on the contents included */ export default class ExtrinsicPayloadV3 extends Struct { - constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { - super(SignedPayloadDefV3, value); + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | string) { + super(registry, SignedPayloadDefV3, value); } /** diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.spec.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.spec.ts index dff504192491..510894577f63 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.spec.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.spec.ts @@ -2,11 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../../injector'; - +import { TypeRegistry } from '../../../codec'; import ExtrinsicSignature from './ExtrinsicSignature'; describe('ExtrinsicSignatureV3', (): void => { + const registry = new TypeRegistry(); + it('encodes to a sane Uint8Array', (): void => { const u8a = new Uint8Array([ // signer as an AccountIndex @@ -23,7 +24,7 @@ describe('ExtrinsicSignatureV3', (): void => { ]); expect( - new ExtrinsicSignature(u8a, { isSigned: true }).toU8a() + new ExtrinsicSignature(registry, u8a, { isSigned: true }).toU8a() ).toEqual(u8a); }); }); diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts index bacd6269d40f..afc64900b55b 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts @@ -21,9 +21,9 @@ export default class ExtrinsicSignatureV3 extends ExtrinsicSignatureV2 { */ public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { return this.injectSignature( - createType('Address', signer), - createType('Signature', signature), - new ExtrinsicPayloadV3(payload) + createType(this.registry, 'Address', signer), + createType(this.registry, 'Signature', signature), + new ExtrinsicPayloadV3(this.registry, payload) ); } @@ -31,8 +31,8 @@ export default class ExtrinsicSignatureV3 extends ExtrinsicSignatureV2 { * @description Generate a payload and pplies the signature from a keypair */ public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): IExtrinsicSignature { - const signer = createType('Address', account.publicKey); - const payload = new ExtrinsicPayloadV3({ + const signer = createType(this.registry, 'Address', account.publicKey); + const payload = new ExtrinsicPayloadV3(this.registry, { blockHash, era: era || IMMORTAL_ERA, genesisHash, @@ -41,7 +41,7 @@ export default class ExtrinsicSignatureV3 extends ExtrinsicSignatureV2 { specVersion, tip: tip || 0 }); - const signature = createType('Signature', payload.sign(account)); + const signature = createType(this.registry, 'Signature', payload.sign(account)); return this.injectSignature(signer, signature, payload); } diff --git a/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts index 360cf2247096..13ad9a1f559a 100644 --- a/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts +++ b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts @@ -2,34 +2,33 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../../injector'; - import BN from 'bn.js'; import testingPairs from '@polkadot/keyring/testingPairs'; import Decorated from '@polkadot/metadata'; import Metadata from '@polkadot/metadata/Metadata'; import metadataStatic from '@polkadot/metadata/Metadata/static'; -import Call from '../../Generic/Call'; +import { TypeRegistry } from '../../../codec/create'; import Extrinsic from './Extrinsic'; -const decorated = new Decorated(metadataStatic); +const registry = new TypeRegistry(); +const decorated = new Decorated(registry, metadataStatic); const keyring = testingPairs({ type: 'ed25519' }, false); -describe('ExtrinsicV4', (): void => { - beforeEach((): void => { - Call.injectMetadata(new Metadata(metadataStatic)); - }); +// eslint-disable-next-line no-new +new Metadata(registry, metadataStatic); +describe('ExtrinsicV4', (): void => { it('constructs a sane Uint8Array (default)', (): void => { expect( - new Extrinsic().toU8a() + new Extrinsic(registry).toU8a() ).toEqual(new Uint8Array([0, 0])); }); it('creates a unsigned extrinsic', (): void => { expect( new Extrinsic( + registry, decorated.tx.balances.transfer(keyring.bob.publicKey, 6969) ).toHex() ).toEqual( @@ -44,6 +43,7 @@ describe('ExtrinsicV4', (): void => { it('creates a signed extrinsic', (): void => { expect( new Extrinsic( + registry, decorated.tx.balances.transfer(keyring.bob.publicKey, 6969) ).sign(keyring.alice, { blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', diff --git a/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts index 8cafecc4fc8f..8b0164815a3f 100644 --- a/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts +++ b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Call } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicOptions } from '../types'; import { isU8a } from '@polkadot/util'; @@ -25,24 +25,24 @@ export interface ExtrinsicValueV4 { * The third generation of compact extrinsics */ export default class ExtrinsicV4 extends Struct implements IExtrinsicImpl { - constructor (value?: Uint8Array | ExtrinsicValueV4 | Call, { isSigned }: Partial = {}) { - super({ + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV4 | Call, { isSigned }: Partial = {}) { + super(registry, { signature: ExtrinsicSignatureV4, method: 'Call' - }, ExtrinsicV4.decodeExtrinsic(value, isSigned)); + }, ExtrinsicV4.decodeExtrinsic(registry, value, isSigned)); } - public static decodeExtrinsic (value?: Call | Uint8Array | ExtrinsicValueV4, isSigned = false): ExtrinsicValueV4 { + public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV4, isSigned = false): ExtrinsicValueV4 { if (!value) { return {}; } else if (value instanceof ExtrinsicV4) { return value; - } else if (value instanceof ClassOf('Call')) { + } else if (value instanceof ClassOf(registry, 'Call')) { return { method: value }; } else if (isU8a(value)) { // here we decode manually since we need to pull through the version information - const signature = new ExtrinsicSignatureV4(value, { isSigned }); - const method = createType('Call', value.subarray(signature.encodedLength)); + const signature = new ExtrinsicSignatureV4(registry, value, { isSigned }); + const method = createType(registry, 'Call', value.subarray(signature.encodedLength)); return { method, diff --git a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts index 4db03bc12aaa..023e8c45eafd 100644 --- a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Balance, ExtrinsicEra, Hash, Index } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IKeyringPair } from '../../../types'; +import { ExtrinsicPayloadValue, IKeyringPair, Registry } from '../../../types'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; @@ -19,8 +19,8 @@ import { SignedPayloadDefV3 as SignedPayloadDefV4 } from '../v3/ExtrinsicPayload * on the contents included */ export default class ExtrinsicPayloadV4 extends Struct { - constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { - super(SignedPayloadDefV4, value); + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | string) { + super(registry, SignedPayloadDefV4, value); } /** diff --git a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts index f016c60079c3..340df1a716a8 100644 --- a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts +++ b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts @@ -2,11 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../../injector'; - +import { TypeRegistry } from '../../../codec'; import ExtrinsicSignature from './ExtrinsicSignature'; describe('ExtrinsicSignatureV4', (): void => { + const registry = new TypeRegistry(); + it('encodes to a sane Uint8Array', (): void => { const u8a = new Uint8Array([ // signer as an AccountIndex @@ -25,7 +26,7 @@ describe('ExtrinsicSignatureV4', (): void => { ]); expect( - new ExtrinsicSignature(u8a, { isSigned: true }).toU8a() + new ExtrinsicSignature(registry, u8a, { isSigned: true }).toU8a() ).toEqual(u8a); }); }); diff --git a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts index ba1a2361b782..b044a186588d 100644 --- a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Address, Balance, Call, EcdsaSignature, Ed25519Signature, ExtrinsicEra, Index, MultiSignature, Sr25519Signature } from '../../../interfaces/runtime'; -import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, Registry, SignatureOptions } from '../../../types'; import { ExtrinsicSignatureOptions } from '../types'; import { createType } from '../../../codec/create'; @@ -18,8 +18,8 @@ import ExtrinsicPayloadV4 from './ExtrinsicPayload'; * A container for the [[Signature]] associated with a specific [[Extrinsic]] */ export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSignature { - constructor (value: ExtrinsicSignatureV4 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { - super({ + constructor (registry: Registry, value: ExtrinsicSignatureV4 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { + super(registry, { signer: 'Address', signature: 'MultiSignature', era: 'ExtrinsicEra', @@ -113,9 +113,9 @@ export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSi */ public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { return this.injectSignature( - createType('Address', signer), - createType('MultiSignature', signature), - new ExtrinsicPayloadV4(payload) + createType(this.registry, 'Address', signer), + createType(this.registry, 'MultiSignature', signature), + new ExtrinsicPayloadV4(this.registry, payload) ); } @@ -123,8 +123,8 @@ export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSi * @description Generate a payload and applies the signature from a keypair */ public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): IExtrinsicSignature { - const signer = createType('Address', account.publicKey); - const payload = new ExtrinsicPayloadV4({ + const signer = createType(this.registry, 'Address', account.publicKey); + const payload = new ExtrinsicPayloadV4(this.registry, { blockHash, era: era || IMMORTAL_ERA, genesisHash, @@ -133,7 +133,7 @@ export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSi specVersion, tip: tip || 0 }); - const signature = createType('MultiSignature', payload.sign(account)); + const signature = createType(this.registry, 'MultiSignature', payload.sign(account)); return this.injectSignature(signer, signature, payload); } diff --git a/packages/types/src/primitive/Generic/AccountId.spec.ts b/packages/types/src/primitive/Generic/AccountId.spec.ts index 9eceb18acd64..e95b6939c9d0 100644 --- a/packages/types/src/primitive/Generic/AccountId.spec.ts +++ b/packages/types/src/primitive/Generic/AccountId.spec.ts @@ -2,18 +2,18 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import { setSS58Format } from '@polkadot/util-crypto'; -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; import U8a from '../../codec/U8a'; import jsonVec from '../../json/AccountIdVec.001.json'; import AccountId from './AccountId'; describe('AccountId', (): void => { + const registry = new TypeRegistry(); + describe('defaults', (): void => { - const id = createType('AccountId'); + const id = createType(registry, 'AccountId'); it('has a 32-byte length', (): void => { expect(id).toHaveLength(32); @@ -31,20 +31,20 @@ describe('AccountId', (): void => { describe('decoding', (): void => { const testDecode = (type: string, input: Uint8Array | string | AccountId, expected: string): void => it(`can decode from ${type}`, (): void => { - const a = createType('AccountId', input); + const a = createType(registry, 'AccountId', input); expect(a.toString()).toBe(expected); }); testDecode( 'AccountId', - createType('AccountId', '0x0102030405060708010203040506070801020304050607080102030405060708'), + createType(registry, 'AccountId', '0x0102030405060708010203040506070801020304050607080102030405060708'), '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' ); testDecode('hex', '0x0102030405060708010203040506070801020304050607080102030405060708', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); testDecode('string', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); testDecode( 'U8a', - new U8a([ + new U8a(registry, [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 ]), @@ -63,7 +63,7 @@ describe('AccountId', (): void => { describe('encoding', (): void => { const testEncode = (to: 'toHex' | 'toJSON' | 'toString' | 'toU8a', expected: Uint8Array | string, input = '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'): void => it(`can encode ${to}`, (): void => { - const a = createType('AccountId', input); + const a = createType(registry, 'AccountId', input); expect(a[to]()).toEqual(expected); }); @@ -78,7 +78,7 @@ describe('AccountId', (): void => { ])); it('decodes to a non-empty value', (): void => { - expect(createType('AccountId', '7qT1BvpawNbqb3BZaBTMFMMAKrpJKLPf1LmEHR1JyarWJdMX').isEmpty).toBe(false); + expect(createType(registry, 'AccountId', '7qT1BvpawNbqb3BZaBTMFMMAKrpJKLPf1LmEHR1JyarWJdMX').isEmpty).toBe(false); }); }); @@ -86,8 +86,8 @@ describe('AccountId', (): void => { it('has the correct entries', (): void => { setSS58Format(68); - const data = createType('StorageData', jsonVec.params.result.changes[0][1]); - const list = createType('Vec', data).map((accountId): string => accountId.toString()); + const data = createType(registry, 'StorageData', jsonVec.params.result.changes[0][1]); + const list = createType(registry, 'Vec', data).map((accountId): string => accountId.toString()); expect(list).toEqual([ '7qVJujLF3EDbZt5WfQXWvueFedMS4Vfk2Hb4GyR8jwksTLup', diff --git a/packages/types/src/primitive/Generic/AccountId.ts b/packages/types/src/primitive/Generic/AccountId.ts index a885baf270dd..8043dabb745f 100644 --- a/packages/types/src/primitive/Generic/AccountId.ts +++ b/packages/types/src/primitive/Generic/AccountId.ts @@ -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 { AnyString, AnyU8a } from '../../types'; +import { AnyString, AnyU8a, Registry } from '../../types'; import { hexToU8a, isHex, isString, isU8a, u8aToU8a } from '@polkadot/util'; import { decodeAddress, encodeAddress } from '@polkadot/util-crypto'; @@ -17,11 +17,8 @@ import U8aFixed from '../../codec/U8aFixed'; * just a Uint8Array wrapper with a fixed length. */ export default class AccountId extends U8aFixed { - constructor (value: AnyU8a = new Uint8Array()) { - super( - AccountId.decodeAccountId(value), - 256 - ); + constructor (registry: Registry, value: AnyU8a = new Uint8Array()) { + super(registry, AccountId.decodeAccountId(value), 256); } public static encode (value: Uint8Array): string { diff --git a/packages/types/src/primitive/Generic/AccountIndex.spec.ts b/packages/types/src/primitive/Generic/AccountIndex.spec.ts index d7bb9dc71144..9d8496fd06fa 100644 --- a/packages/types/src/primitive/Generic/AccountIndex.spec.ts +++ b/packages/types/src/primitive/Generic/AccountIndex.spec.ts @@ -2,44 +2,44 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import BN from 'bn.js'; -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; import AccountIndex from './AccountIndex'; describe('AccountIndex', (): void => { + const registry = new TypeRegistry(); + it('creates a BN representation', (): void => { expect( - createType('AccountIndex', new Uint8Array([17, 18, 19, 20])).toNumber() + createType(registry, 'AccountIndex', new Uint8Array([17, 18, 19, 20])).toNumber() ).toEqual(336794129); }); it('creates a BN representation (from ss-58)', (): void => { expect( - createType('AccountIndex', 'Mwz15xP2').toNumber() + createType(registry, 'AccountIndex', 'Mwz15xP2').toNumber() ).toEqual(336794129); }); it('constructs 2-byte from number', (): void => { expect( - createType('AccountIndex', 256 * 1).toString() + createType(registry, 'AccountIndex', 256 * 1).toString() ).toEqual('25GUyv'); }); it('constructs from number', (): void => { expect( - createType('AccountIndex', new BN(336794129)).toString() + createType(registry, 'AccountIndex', new BN(336794129)).toString() ).toEqual('Mwz15xP2'); }); it('compares ss-58 values', (): void => { - expect(createType('AccountIndex', 256 * 1).eq('25GUyv')).toBe(true); + expect(createType(registry, 'AccountIndex', 256 * 1).eq('25GUyv')).toBe(true); }); it('compares numbers', (): void => { - expect(createType('AccountIndex', '2jpAFn').eq(256 * 1)).toBe(true); + expect(createType(registry, 'AccountIndex', '2jpAFn').eq(256 * 1)).toBe(true); }); describe('calcLength', (): void => { diff --git a/packages/types/src/primitive/Generic/AccountIndex.ts b/packages/types/src/primitive/Generic/AccountIndex.ts index 55bb46b180f0..45e64f4cd9a3 100644 --- a/packages/types/src/primitive/Generic/AccountIndex.ts +++ b/packages/types/src/primitive/Generic/AccountIndex.ts @@ -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 { AnyNumber } from '../../types'; +import { AnyNumber, Registry } from '../../types'; import BN from 'bn.js'; import { bnToBn, isBn, isNumber, isU8a, isHex } from '@polkadot/util'; @@ -28,10 +28,8 @@ const MAX_4BYTE = new BN(1).shln(32); * for an Account. We extends from [[U32]] to provide the number-like properties. */ export default class AccountIndex extends U32 { - constructor (value: AnyNumber = new BN(0)) { - super( - AccountIndex.decodeAccountIndex(value) - ); + constructor (registry: Registry, value: AnyNumber = new BN(0)) { + super(registry, AccountIndex.decodeAccountIndex(value)); } public static decodeAccountIndex (value: AnyNumber): BN | Uint8Array | number | string { @@ -93,7 +91,7 @@ export default class AccountIndex extends U32 { } // convert and compare - return super.eq(createType('AccountIndex', other)); + return super.eq(createType(this.registry, 'AccountIndex', other)); } /** diff --git a/packages/types/src/primitive/Generic/Address.spec.ts b/packages/types/src/primitive/Generic/Address.spec.ts index 4f5d3598695d..6c97ebf5dd33 100644 --- a/packages/types/src/primitive/Generic/Address.spec.ts +++ b/packages/types/src/primitive/Generic/Address.spec.ts @@ -2,17 +2,17 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - -import { createType } from '../../codec/create'; +import { createType, TypeRegistry } from '../../codec/create'; import AccountId from './AccountId'; import AccountIndex from './AccountIndex'; import Address from './Address'; describe('Address', (): void => { + const registry = new TypeRegistry(); + const testDecode = (type: string, input: Address | AccountId | AccountIndex | number[] | Uint8Array, expected: string): void => it(`can decode from ${type}`, (): void => { - const a = createType('Address', input); + const a = createType(registry, 'Address', input); expect(a.toString()).toBe(expected); }); @@ -20,35 +20,35 @@ describe('Address', (): void => { it('equals on AccountId', (): void => { const addr = '5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo'; - expect(createType('Address', addr).eq(addr)).toBe(true); + expect(createType(registry, 'Address', addr).eq(addr)).toBe(true); }); it('equals on AccountIndex', (): void => { // see the test below - these are equivalent (with different prefix encoding) - expect(createType('Address', '2jpAFn').eq('25GUyv')).toBe(true); + expect(createType(registry, 'Address', '2jpAFn').eq('25GUyv')).toBe(true); }); }); describe('decoding', (): void => { testDecode( 'Address', - createType('Address', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'), + createType(registry, 'Address', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'), '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' ); testDecode( 'AccountId', - createType('AccountId', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'), + createType(registry, 'AccountId', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'), '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' ); testDecode( 'AccountIndex (mixed prefixes)', - createType('Address', '2jpAFn'), + createType(registry, 'Address', '2jpAFn'), // NOTE Expected adress here is encoded with prefix 42, input above with 68 '25GUyv' ); testDecode( 'AccountIndex (hex)', - createType('AccountIndex', '0x0100'), + createType(registry, 'AccountIndex', '0x0100'), '25GUyv' ); testDecode( @@ -97,7 +97,7 @@ describe('Address', (): void => { describe('encoding', (): void => { const testEncode = (to: 'toHex' | 'toString' | 'toU8a', expected: string | Uint8Array): void => it(`can encode ${to}`, (): void => { - const a = createType('Address', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); + const a = createType(registry, 'Address', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); expect(a[to]()).toEqual(expected); }); diff --git a/packages/types/src/primitive/Generic/Address.ts b/packages/types/src/primitive/Generic/Address.ts index e64c9b41dc4d..b3b1e5517a4f 100644 --- a/packages/types/src/primitive/Generic/Address.ts +++ b/packages/types/src/primitive/Generic/Address.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '../../types'; + import BN from 'bn.js'; import { isBn, isHex, isNumber, isU8a, u8aConcat, u8aToHex, u8aToU8a, u8aToBn } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; @@ -24,46 +26,44 @@ export const ACCOUNT_ID_PREFIX = new Uint8Array([0xff]); * is encoded as `[ , ...publicKey/...bytes ]` as per spec */ export default class Address extends Base { - constructor (value: AnyAddress = new Uint8Array()) { - super( - Address.decodeAddress(value) - ); + constructor (registry: Registry, value: AnyAddress = new Uint8Array()) { + super(registry, Address.decodeAddress(registry, value)); } - public static decodeAddress (value: AnyAddress): AccountId | AccountIndex { + public static decodeAddress (registry: Registry, value: AnyAddress): AccountId | AccountIndex { if (value instanceof AccountId || value instanceof AccountIndex) { return value; } else if (value instanceof Address) { return value.raw; } else if (isBn(value) || isNumber(value)) { - return createType('AccountIndex', value); + return createType(registry, 'AccountIndex', value); } else if (Array.isArray(value) || isHex(value) || isU8a(value)) { - return Address.decodeU8a(u8aToU8a(value)); + return Address.decodeU8a(registry, u8aToU8a(value)); } - return Address.decodeString(value); + return Address.decodeString(registry, value); } - private static decodeString (value: string): AccountId | AccountIndex { + private static decodeString (registry: Registry, value: string): AccountId | AccountIndex { const decoded = decodeAddress(value); return decoded.length === 32 - ? createType('AccountId', decoded) - : createType('AccountIndex', u8aToBn(decoded, true)); + ? createType(registry, 'AccountId', decoded) + : createType(registry, 'AccountIndex', u8aToBn(decoded, true)); } - private static decodeU8a (value: Uint8Array): AccountId | AccountIndex { + private static decodeU8a (registry: Registry, value: Uint8Array): AccountId | AccountIndex { // This allows us to instantiate an address with a raw publicKey. Do this first before // we checking the first byte, otherwise we may split an already-existent valid address if (value.length === 32) { - return createType('AccountId', value); + return createType(registry, 'AccountId', value); } else if (value[0] === 0xff) { - return createType('AccountId', value.subarray(1)); + return createType(registry, 'AccountId', value.subarray(1)); } const [offset, length] = AccountIndex.readLength(value); - return createType('AccountIndex', u8aToBn(value.subarray(offset, offset + length), true)); + return createType(registry, 'AccountIndex', u8aToBn(value.subarray(offset, offset + length), true)); } /** diff --git a/packages/types/src/primitive/Generic/Block.spec.ts b/packages/types/src/primitive/Generic/Block.spec.ts index ba0ad07a30f3..0e0b26a753ed 100644 --- a/packages/types/src/primitive/Generic/Block.spec.ts +++ b/packages/types/src/primitive/Generic/Block.spec.ts @@ -2,23 +2,22 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import Metadata from '@polkadot/metadata/Metadata'; -import rpcMetadata from '@polkadot/metadata/Metadata/static'; +import metadataStatic from '@polkadot/metadata/Metadata/static'; +import { TypeRegistry } from '../../codec'; import block00300 from '../../json/SignedBlock.003.00.json'; -import Call from './Call'; import Block from './Block'; -describe('Block', (): void => { - beforeEach((): void => { - Call.injectMetadata(new Metadata(rpcMetadata)); - }); +const registry = new TypeRegistry(); +// eslint-disable-next-line no-new +new Metadata(registry, metadataStatic); + +describe('Block', (): void => { it('has a valid toRawType', (): void => { expect( - new Block().toRawType() + new Block(registry).toRawType() ).toEqual( // each of the containing structures have been stringified on their own JSON.stringify({ @@ -37,7 +36,7 @@ describe('Block', (): void => { }); it('re-encodes digest items correctly', (): void => { - const digest = new Block(block00300.result.block).header.digest; + const digest = new Block(registry, block00300.result.block).header.digest; expect(digest.logs[0].toHex()).toEqual(block00300.result.block.header.digest.logs[0]); expect(digest.logs[1].toHex()).toEqual(block00300.result.block.header.digest.logs[1]); diff --git a/packages/types/src/primitive/Generic/Block.ts b/packages/types/src/primitive/Generic/Block.ts index d5f6cb2133e1..a83005ad0781 100644 --- a/packages/types/src/primitive/Generic/Block.ts +++ b/packages/types/src/primitive/Generic/Block.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Digest, DigestItem, Hash, Header } from '../../interfaces/runtime'; -import { AnyNumber, AnyU8a } from '../../types'; +import { AnyNumber, AnyU8a, Registry } from '../../types'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -31,8 +31,8 @@ export interface BlockValue { * A block encoded with header and extrinsics */ export default class Block extends Struct { - constructor (value?: BlockValue | Uint8Array) { - super({ + constructor (registry: Registry, value?: BlockValue | Uint8Array) { + super(registry, { header: 'Header', extrinsics: 'Vec' }, value); @@ -42,7 +42,7 @@ export default class Block extends Struct { * @description Encodes a content [[Hash]] for the block */ public get contentHash (): Hash { - return createType('Hash', blake2AsU8a(this.toU8a(), 256)); + return createType(this.registry, 'Hash', blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/primitive/Generic/Call.spec.ts b/packages/types/src/primitive/Generic/Call.spec.ts index 8ecf798069e5..f6cffadad049 100644 --- a/packages/types/src/primitive/Generic/Call.spec.ts +++ b/packages/types/src/primitive/Generic/Call.spec.ts @@ -2,21 +2,21 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - import Metadata from '@polkadot/metadata/Metadata'; import metadataStatic from '@polkadot/metadata/Metadata/static'; +import { TypeRegistry } from '../../codec'; import Call from './Call'; -describe('Call', (): void => { - beforeEach((): void => { - Call.injectMetadata(new Metadata(metadataStatic)); - }); +const registry = new TypeRegistry(); +// eslint-disable-next-line no-new +new Metadata(registry, metadataStatic); + +describe('Call', (): void => { it('handles decoding correctly (bare)', (): void => { expect( - new Call({ + new Call(registry, { args: [], callIndex: [6, 1] // balances.setBalance }).toU8a() @@ -25,7 +25,7 @@ describe('Call', (): void => { it('handles creation from a hex value properly', (): void => { expect( - new Call('0x0601').toU8a() + new Call(registry, '0x0601').toU8a() ).toEqual(new Uint8Array([6, 1, 0, 0, 0])); // balances.setBalance }); @@ -37,19 +37,19 @@ describe('Call', (): void => { it('is false with no arguments', (): void => { expect( - new Call(test, { args: [] } as any).hasOrigin + new Call(registry, test, { args: [] } as any).hasOrigin ).toEqual(false); }); it('is false with first argument as non-Origin', (): void => { expect( - new Call(test, { args: [{ name: 'a', type: 'u32' }] } as any).hasOrigin + new Call(registry, test, { args: [{ name: 'a', type: 'u32' }] } as any).hasOrigin ).toEqual(false); }); it('is false with first argument as non-Origin', (): void => { expect( - new Call(test, { args: [{ name: 'a', type: 'Origin' }] } as any).hasOrigin + new Call(registry, test, { args: [{ name: 'a', type: 'Origin' }] } as any).hasOrigin ).toEqual(true); }); }); diff --git a/packages/types/src/primitive/Generic/Call.ts b/packages/types/src/primitive/Generic/Call.ts index 3679fdedabd6..f46feac20af3 100644 --- a/packages/types/src/primitive/Generic/Call.ts +++ b/packages/types/src/primitive/Generic/Call.ts @@ -3,14 +3,10 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { FunctionArgumentMetadataLatest, FunctionMetadataLatest } from '../../interfaces/metadata'; -import { AnyU8a, ArgsDef, CallFunction, Codec, IMethod } from '../../types'; +import { AnyU8a, ArgsDef, Codec, IMethod, Registry } from '../../types'; -// FIXME Here we unfortunately cannot use Decorated, because that already calls -// fromMetadata for storage, and we have then a type import ordering problem -import extrinsicsFromMeta from '@polkadot/metadata/Decorated/extrinsics/fromMetadata'; -import { assert, isHex, isObject, isU8a, u8aToU8a } from '@polkadot/util'; +import { isHex, isObject, isU8a, u8aToU8a } from '@polkadot/util'; -import Metadata from '@polkadot/metadata/Metadata'; import { getTypeDef, getTypeClass } from '../../codec/create'; import Struct from '../../codec/Struct'; import U8aFixed from '../../codec/U8aFixed'; @@ -25,21 +21,14 @@ interface DecodedMethod extends DecodeMethodInput { meta: FunctionMetadataLatest; } -const FN_UNKNOWN: Partial = { - method: 'unknown', - section: 'unknown' -}; - -const injected: Record = {}; - /** * @name CallIndex * @description * A wrapper around the `[sectionIndex, methodIndex]` value that uniquely identifies a method */ export class CallIndex extends U8aFixed { - constructor (value?: AnyU8a) { - super(value, 16); + constructor (registry: Registry, value?: AnyU8a) { + super(registry, value, 16); } } @@ -52,10 +41,10 @@ export class CallIndex extends U8aFixed { export default class Call extends Struct implements IMethod { protected _meta: FunctionMetadataLatest; - constructor (value: any, meta?: FunctionMetadataLatest) { - const decoded = Call.decodeCall(value, meta); + constructor (registry: Registry, value: any, meta?: FunctionMetadataLatest) { + const decoded = Call.decodeCall(registry, value, meta); - super({ + super(registry, { callIndex: CallIndex, args: Struct.with(decoded.argsDef) }, decoded); @@ -73,17 +62,17 @@ export default class Call extends Struct implements IMethod { * @param _meta - Metadata to use, so that `injectMethods` lookup is not * necessary. */ - private static decodeCall (value: DecodedMethod | Uint8Array | string = new Uint8Array(), _meta?: FunctionMetadataLatest): DecodedMethod { + private static decodeCall (registry: Registry, value: DecodedMethod | Uint8Array | string = new Uint8Array(), _meta?: FunctionMetadataLatest): DecodedMethod { if (isHex(value) || isU8a(value)) { - return Call.decodeCallViaU8a(u8aToU8a(value), _meta); + return Call.decodeCallViaU8a(registry, u8aToU8a(value), _meta); } else if (isObject(value) && value.callIndex && value.args) { - return Call.decodeCallViaObject(value, _meta); + return Call.decodeCallViaObject(registry, value, _meta); } throw new Error(`Call: Cannot decode value '${value}' of type ${typeof value}`); } - private static decodeCallViaObject (value: DecodedMethod, _meta?: FunctionMetadataLatest): DecodedMethod { + private static decodeCallViaObject (registry: Registry, value: DecodedMethod, _meta?: FunctionMetadataLatest): DecodedMethod { // we only pass args/methodsIndex out const { args, callIndex } = value; @@ -93,26 +82,26 @@ export default class Call extends Struct implements IMethod { : callIndex; // Find metadata with callIndex - const meta = _meta || Call.findFunction(lookupIndex).meta; + const meta = _meta || registry.findMetaCall(lookupIndex).meta; return { args, - argsDef: Call.getArgsDef(meta), + argsDef: Call.getArgsDef(registry, meta), meta, callIndex }; } - private static decodeCallViaU8a (value: Uint8Array, _meta?: FunctionMetadataLatest): DecodedMethod { + private static decodeCallViaU8a (registry: Registry, value: Uint8Array, _meta?: FunctionMetadataLatest): DecodedMethod { // The first 2 bytes are the callIndex const callIndex = value.subarray(0, 2); // Find metadata with callIndex - const meta = _meta || Call.findFunction(callIndex).meta; + const meta = _meta || registry.findMetaCall(callIndex).meta; return { args: value.subarray(2), - argsDef: Call.getArgsDef(meta), + argsDef: Call.getArgsDef(registry, meta), callIndex, meta }; @@ -128,49 +117,21 @@ export default class Call extends Struct implements IMethod { : []; } - // We could only inject the meta (see injectMethods below) and then do a - // meta-only lookup via - // - // metadata.modules[callIndex[0]].module.call.functions[callIndex[1]] - // - // As a convenience helper though, we return the full constructor function, - // which includes the meta, name, section & actual interface for calling - public static findFunction (callIndex: Uint8Array): CallFunction { - assert(Object.keys(injected).length > 0, 'Calling Call.findFunction before extrinsics have been injected.'); - - return injected[callIndex.toString()] || FN_UNKNOWN; - } - /** * Get a mapping of `argument name -> argument type` for the function, from * its metadata. * * @param meta - The function metadata used to get the definition. */ - private static getArgsDef (meta: FunctionMetadataLatest): ArgsDef { + private static getArgsDef (registry: Registry, meta: FunctionMetadataLatest): ArgsDef { return Call.filterOrigin(meta).reduce((result, { name, type }): ArgsDef => { - const Type = getTypeClass( - getTypeDef(type.toString()) - ); + const Type = getTypeClass(registry, getTypeDef(type.toString())); result[name.toString()] = Type; return result; }, {} as unknown as ArgsDef); } - // FIXME Should take the Decorated metadata (`import Metadata from '@polkadot/metadata'`) - // instead of the Codec Metadata - // https://github.com/polkadot-js/api/pull/1463#pullrequestreview-300618425 - public static injectMetadata (metadata: Metadata): void { - const extrinsics = extrinsicsFromMeta(metadata); - - Object.values(extrinsics).forEach((methods): void => - Object.values(methods).forEach((method): void => { - injected[method.callIndex.toString()] = method; - }) - ); - } - /** * @description The arguments for the function call */ @@ -183,7 +144,7 @@ export default class Call extends Struct implements IMethod { * @description The argument definitions */ public get argsDef (): ArgsDef { - return Call.getArgsDef(this.meta); + return Call.getArgsDef(this.registry, this.meta); } /** @@ -220,14 +181,14 @@ export default class Call extends Struct implements IMethod { * @description Returns the name of the method */ public get methodName (): string { - return Call.findFunction(this.callIndex).method; + return this.registry.findMetaCall(this.callIndex).method; } /** * @description Returns the module containing the method */ public get sectionName (): string { - return Call.findFunction(this.callIndex).section; + return this.registry.findMetaCall(this.callIndex).section; } /** diff --git a/packages/types/src/primitive/Generic/ConsensusEngineId.spec.ts b/packages/types/src/primitive/Generic/ConsensusEngineId.spec.ts index ddbdca431bd7..79d19b4005b4 100644 --- a/packages/types/src/primitive/Generic/ConsensusEngineId.spec.ts +++ b/packages/types/src/primitive/Generic/ConsensusEngineId.spec.ts @@ -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 '../../injector'; - import ConsensusEngineId, { CID_AURA, CID_BABE, CID_GRPA } from './ConsensusEngineId'; describe('ConsensusEngineId', (): void => { diff --git a/packages/types/src/primitive/Generic/ConsensusEngineId.ts b/packages/types/src/primitive/Generic/ConsensusEngineId.ts index 2ad12f8482c9..4d7f50a3c846 100644 --- a/packages/types/src/primitive/Generic/ConsensusEngineId.ts +++ b/packages/types/src/primitive/Generic/ConsensusEngineId.ts @@ -60,14 +60,14 @@ export default class ConsensusEngineId extends U32 { private getAuraAuthor (bytes: Bytes, sessionValidators: AccountId[]): AccountId { return sessionValidators[ - createType('RawAuraPreDigest', bytes.toU8a(true)) + createType(this.registry, 'RawAuraPreDigest', bytes.toU8a(true)) .slotNumber .modn(sessionValidators.length) ]; } private getBabeAuthor (bytes: Bytes, sessionValidators: AccountId[]): AccountId { - const digest = createType('RawBabePreDigestCompat', bytes.toU8a(true)); + const digest = createType(this.registry, 'RawBabePreDigestCompat', bytes.toU8a(true)); return sessionValidators[ (digest.value as U32).toNumber() diff --git a/packages/types/src/primitive/Generic/Digest.spec.ts b/packages/types/src/primitive/Generic/Digest.spec.ts index 74d5fb83db7c..3d5d92c7f16b 100644 --- a/packages/types/src/primitive/Generic/Digest.spec.ts +++ b/packages/types/src/primitive/Generic/Digest.spec.ts @@ -2,15 +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 '../../injector'; - +import { TypeRegistry } from '../../codec'; import json3 from '../../json/Header.003.json'; import block1 from '../../json/SignedBlock.003.00.json'; import Digest from './Digest'; describe('Digest', (): void => { + const registry = new TypeRegistry(); + it('decodes logs with consensus', (): void => { - const digest = new Digest(json3.result.digest); + const digest = new Digest(registry, json3.result.digest); expect(digest.logs.length).toEqual(1); @@ -24,14 +25,14 @@ describe('Digest', (): void => { }); it('filters logs, excluding items', (): void => { - const logs = new Digest(block1.result.block.header.digest).logsWith('PreRuntime'); + const logs = new Digest(registry, block1.result.block.header.digest).logsWith('PreRuntime'); expect(logs.length).toEqual(1); expect(logs[0].type).toEqual('PreRuntime'); }); it('filters logs, including items', (): void => { - const logs = new Digest(block1.result.block.header.digest).logsWithout('PreRuntime'); + const logs = new Digest(registry, block1.result.block.header.digest).logsWithout('PreRuntime'); expect(logs.length).toEqual(1); expect(logs[0].type).toEqual('Seal'); diff --git a/packages/types/src/primitive/Generic/Digest.ts b/packages/types/src/primitive/Generic/Digest.ts index 81e894b66c1b..59342efadbb4 100644 --- a/packages/types/src/primitive/Generic/Digest.ts +++ b/packages/types/src/primitive/Generic/Digest.ts @@ -4,6 +4,7 @@ import { AuthorityId } from '../../interfaces/consensus'; import { Consensus, Hash, PreRuntime, Seal, SealV0 } from '../../interfaces/runtime'; +import { Registry } from '../../types'; import { assert } from '@polkadot/util'; @@ -18,8 +19,8 @@ import Bytes from '../Bytes'; * A [[Enum]] the specifies the specific item in the logs of a [[Digest]] */ export class DigestItem extends Enum { - constructor (value: any) { - super({ + constructor (registry: Registry, value: any) { + super(registry, { Other: 'Bytes', // 0 AuthoritiesChange: 'Vec', // 1 ChangesTrieRoot: 'Hash', // 2 @@ -163,8 +164,8 @@ export class DigestItem extends Enum { * A [[Header]] Digest */ export default class Digest extends Struct { - constructor (value: any) { - super({ + constructor (registry: Registry, value: any) { + super(registry, { logs: Vec.with(DigestItem) }, value); } diff --git a/packages/types/src/primitive/Generic/Event.ts b/packages/types/src/primitive/Generic/Event.ts index 3d5cac0acf45..ebd1e4d52a62 100644 --- a/packages/types/src/primitive/Generic/Event.ts +++ b/packages/types/src/primitive/Generic/Event.ts @@ -5,18 +5,12 @@ import { TypeDef } from '../../codec/types'; import { EventMetadataLatest } from '../../interfaces/metadata'; import { EventId } from '../../interfaces/system'; -import { Constructor, Codec } from '../../types'; +import { Constructor, Registry, RegistryMetadataEvent } from '../../types'; -import Metadata from '@polkadot/metadata/Metadata'; -import { assert, isUndefined, stringCamelCase, u8aToHex } from '@polkadot/util'; - -import { getTypeClass, getTypeDef } from '../../codec/create'; import Struct from '../../codec/Struct'; import Tuple from '../../codec/Tuple'; import Null from '../Null'; -const EventTypes: Record> = {}; - /** * @name EventData * @description @@ -31,10 +25,10 @@ export class EventData extends Tuple { private _typeDef: TypeDef[]; - constructor (Types: Constructor[], value: Uint8Array, typeDef: TypeDef[], meta: EventMetadataLatest, section: string, method: string) { - super(Types, value); + constructor (registry: Registry, Types: Constructor[], value: Uint8Array, typeDef: TypeDef[], meta: RegistryMetadataEvent, section: string, method: string) { + super(registry, Types, value); - this._meta = meta; + this._meta = meta as EventMetadataLatest; this._method = method; this._section = section; this._typeDef = typeDef; @@ -78,16 +72,16 @@ export class EventData extends Tuple { export default class Event extends Struct { // Currently we _only_ decode from Uint8Array, since we expect it to // be used via EventRecord - constructor (_value?: Uint8Array) { - const { DataType, value } = Event.decodeEvent(_value); + constructor (registry: Registry, _value?: Uint8Array) { + const { DataType, value } = Event.decodeEvent(registry, _value); - super({ + super(registry, { index: 'EventId', data: DataType }, value); } - public static decodeEvent (value: Uint8Array = new Uint8Array()): { DataType: Constructor | Constructor; value?: { index: Uint8Array; data: Uint8Array } } { + public static decodeEvent (registry: Registry, value: Uint8Array = new Uint8Array()): { DataType: Constructor | Constructor; value?: { index: Uint8Array; data: Uint8Array } } { if (!value.length) { return { DataType: Null @@ -95,12 +89,9 @@ export default class Event extends Struct { } const index = value.subarray(0, 2); - const DataType = EventTypes[index.toString()]; - - assert(!isUndefined(DataType), `Unable to decode ${u8aToHex(index)}`); return { - DataType, + DataType: registry.findMetaEvent(index), value: { index, data: value.subarray(2) @@ -108,32 +99,6 @@ export default class Event extends Struct { }; } - // This is called/injected by the API on init, allowing a snapshot of - // the available system events to be used in lookups - // FIXME Should take the Decorated metadata (`import Metadata from '@polkadot/metadata'`) - // instead of the Codec Metadata - // https://github.com/polkadot-js/api/pull/1463#pullrequestreview-300618425 - public static injectMetadata (metadata: Metadata): void { - metadata.asLatest.modules - .filter(({ events }): boolean => events.isSome) - .forEach((section, sectionIndex): void => { - const sectionName = stringCamelCase(section.name.toString()); - - 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.toString())); - const Types = typeDef.map((typeDef): Constructor => getTypeClass(typeDef)); - - EventTypes[eventIndex.toString()] = class extends EventData { - constructor (value: Uint8Array) { - super(Types, value, typeDef, meta, sectionName, methodName); - } - }; - }); - }); - } - /** * @description The wrapped [[EventData]] */ diff --git a/packages/types/src/primitive/Generic/Origin.spec.ts b/packages/types/src/primitive/Generic/Origin.spec.ts index 2a384a21960a..49750559df9b 100644 --- a/packages/types/src/primitive/Generic/Origin.spec.ts +++ b/packages/types/src/primitive/Generic/Origin.spec.ts @@ -2,12 +2,15 @@ // 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 '../../codec'; import Origin from './Origin'; describe('Origin', (): void => { + const registry = new TypeRegistry(); + it('does not allow construction', (): void => { expect( - (): Origin => new Origin() + (): Origin => new Origin(registry) ).toThrow(/placeholder/); }); }); diff --git a/packages/types/src/primitive/Generic/Origin.ts b/packages/types/src/primitive/Generic/Origin.ts index d118be61297e..defa9c733fe2 100644 --- a/packages/types/src/primitive/Generic/Origin.ts +++ b/packages/types/src/primitive/Generic/Origin.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '../../types'; + import Null from '../Null'; /** @@ -11,8 +13,8 @@ import Null from '../Null'; * never actually be constructed */ export default class Origin extends Null { - constructor () { - super(); + constructor (registry: Registry) { + super(registry); throw new Error('Origin should not be constructed, it is only a placeholder for compatibility'); } diff --git a/packages/types/src/primitive/Generic/Vote.spec.ts b/packages/types/src/primitive/Generic/Vote.spec.ts index e16da4107a1c..239572725ad6 100644 --- a/packages/types/src/primitive/Generic/Vote.spec.ts +++ b/packages/types/src/primitive/Generic/Vote.spec.ts @@ -2,60 +2,61 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import '../../injector'; - +import { TypeRegistry } from '../../codec'; import Vote from './Vote'; describe('Vote', (): void => { + const registry = new TypeRegistry(); + describe('construction', (): void => { it('constructs via boolean true', (): void => { - expect(new Vote(true).toU8a()).toEqual(new Uint8Array([128])); - expect(new Vote(true).isAye).toBe(true); - expect(new Vote(true).isNay).toBe(false); + expect(new Vote(registry, true).toU8a()).toEqual(new Uint8Array([128])); + expect(new Vote(registry, true).isAye).toBe(true); + expect(new Vote(registry, true).isNay).toBe(false); }); it('constructs via boolean false', (): void => { - expect(new Vote(false).toU8a()).toEqual(new Uint8Array([0])); - expect(new Vote(false).isNay).toBe(true); - expect(new Vote(false).isAye).toBe(false); + expect(new Vote(registry, false).toU8a()).toEqual(new Uint8Array([0])); + expect(new Vote(registry, false).isNay).toBe(true); + expect(new Vote(registry, false).isAye).toBe(false); }); it('has isYay for positive', (): void => { // eslint-disable-next-line no-new-wrappers - expect(new Vote(true).isAye).toBe(true); + expect(new Vote(registry, true).isAye).toBe(true); }); it('has isNay for negative', (): void => { // eslint-disable-next-line no-new-wrappers - expect(new Vote(false).isNay).toBe(true); + expect(new Vote(registry, false).isNay).toBe(true); }); it('is Aye for negative numbers', (): void => { - expect(new Vote(-128).isAye).toBe(true); + expect(new Vote(registry, -128).isAye).toBe(true); }); it('is Nay for positive numbers', (): void => { - expect(new Vote(127).isNay).toBe(true); + expect(new Vote(registry, 127).isNay).toBe(true); }); it('is Nay for 0', (): void => { - expect(new Vote(0).isNay).toBe(true); + expect(new Vote(registry, 0).isNay).toBe(true); }); it('constructs via empty', (): void => { - expect(new Vote().isNay).toBe(true); + expect(new Vote(registry).isNay).toBe(true); }); it('constructs via Uint8Array (empty)', (): void => { - expect(new Vote(new Uint8Array()).isNay).toBe(true); + expect(new Vote(registry, new Uint8Array()).isNay).toBe(true); }); it('constructs via Uint8Array (nay)', (): void => { - expect(new Vote(new Uint8Array([1])).isNay).toBe(true); + expect(new Vote(registry, new Uint8Array([1])).isNay).toBe(true); }); it('constructs via Uint8Array (aye)', (): void => { - const test = new Vote(new Uint8Array([0b10000010])); + const test = new Vote(registry, new Uint8Array([0b10000010])); expect(test.isNay).toBe(false); expect(test.conviction.toString()).toEqual('Locked2x'); @@ -65,7 +66,7 @@ describe('Vote', (): void => { describe('Vote with conviction', (): void => { it('constructs Vote with raw boolean', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 'Locked1x' }).toU8a() @@ -74,7 +75,7 @@ describe('Vote', (): void => { it('constructs with Vote aye is false, conviction is None', (): void => { expect( - new Vote({ + new Vote(registry, { aye: false, conviction: 'None' }).toU8a() @@ -83,7 +84,7 @@ describe('Vote', (): void => { it('constructs with Vote aye is true, conviction is Locked4x', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 'Locked4x' }).toU8a() @@ -94,7 +95,7 @@ describe('Vote', (): void => { describe('getters', (): void => { it('Conviction getter works', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 'Locked2x' }).conviction.toString() @@ -103,7 +104,7 @@ describe('Vote', (): void => { it('Conviction getter works with raw boolean and string conviction', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 'Locked2x' }).conviction.toString() @@ -112,7 +113,7 @@ describe('Vote', (): void => { it('Conviction getter works with raw boolean and conviction index', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 2 }).conviction.toString() @@ -120,7 +121,7 @@ describe('Vote', (): void => { }); it('Conviction getter works with raw boolean and no conviction', (): void => { - const test = new Vote({ aye: true }); + const test = new Vote(registry, { aye: true }); expect(test.isAye).toEqual(true); expect(test.conviction.toString()).toEqual('None'); @@ -128,7 +129,7 @@ describe('Vote', (): void => { it('isAye getter works', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 'None' }).isAye) @@ -137,7 +138,7 @@ describe('Vote', (): void => { it('isNay getter works', (): void => { expect( - new Vote({ + new Vote(registry, { aye: true, conviction: 'None' }).isNay) @@ -147,7 +148,7 @@ describe('Vote', (): void => { describe('utils', (): void => { it('has a sane toRawType', (): void => { - expect(new Vote().toRawType()).toEqual('Vote'); + expect(new Vote(registry).toRawType()).toEqual('Vote'); }); }); }); diff --git a/packages/types/src/primitive/Generic/Vote.ts b/packages/types/src/primitive/Generic/Vote.ts index 0285a1b08549..e1c4b0921dfd 100644 --- a/packages/types/src/primitive/Generic/Vote.ts +++ b/packages/types/src/primitive/Generic/Vote.ts @@ -2,6 +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 { Registry } from '../../types'; import { Conviction } from '../../interfaces/democracy'; import { isBoolean, isNumber, isU8a, isUndefined } from '@polkadot/util'; @@ -32,31 +33,31 @@ export default class Vote extends U8aFixed { private _conviction: Conviction; - constructor (value?: InputTypes) { + constructor (registry: Registry, value?: InputTypes) { // decoded is just 1 byte // Aye: Most Significant Bit // Conviction: 0000 - 0101 - const decoded = Vote.decodeVote(value); + const decoded = Vote.decodeVote(registry, value); - super(decoded, 8); + super(registry, decoded, 8); this._aye = (decoded[0] & AYE_BITS) === AYE_BITS; - this._conviction = createType('Conviction', decoded[0] & CON_MASK); + this._conviction = createType(this.registry, 'Conviction', decoded[0] & CON_MASK); } - private static decodeVote (value?: InputTypes): Uint8Array { + private static decodeVote (registry: Registry, value?: InputTypes): Uint8Array { if (isUndefined(value)) { return Vote.decodeVoteBool(false); } else if (value instanceof Boolean || isBoolean(value)) { - return Vote.decodeVoteBool(new Bool(value).isTrue); + return Vote.decodeVoteBool(new Bool(registry, value).isTrue); } else if (isNumber(value)) { return Vote.decodeVoteBool(value < 0); } else if (isU8a(value)) { return Vote.decodeVoteU8a(value); } - const vote = new Bool(value.aye).isTrue ? AYE_BITS : NAY_BITS; - const conviction = createType('Conviction', isUndefined(value.conviction) ? DEF_CONV : value.conviction); + const vote = new Bool(registry, value.aye).isTrue ? AYE_BITS : NAY_BITS; + const conviction = createType(registry, 'Conviction', isUndefined(value.conviction) ? DEF_CONV : value.conviction); return new Uint8Array([vote | conviction.index]); } diff --git a/packages/types/src/primitive/H160.ts b/packages/types/src/primitive/H160.ts index 9e8e12661645..baa72dfa8d0c 100644 --- a/packages/types/src/primitive/H160.ts +++ b/packages/types/src/primitive/H160.ts @@ -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 { AnyU8a } from '../types'; +import { AnyU8a, Registry } from '../types'; import U8aFixed from '../codec/U8aFixed'; @@ -13,8 +13,8 @@ import U8aFixed from '../codec/U8aFixed'; * as a sane default for fixed-length hash representations. */ export default class H160 extends U8aFixed { - constructor (value?: AnyU8a) { - super(value, 160); + constructor (registry: Registry, value?: AnyU8a) { + super(registry, value, 160); } /** diff --git a/packages/types/src/primitive/H256.ts b/packages/types/src/primitive/H256.ts index b5d7fd878f28..2fbdb7f38d18 100644 --- a/packages/types/src/primitive/H256.ts +++ b/packages/types/src/primitive/H256.ts @@ -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 { AnyU8a, IHash } from '../types'; +import { AnyU8a, IHash, Registry } from '../types'; import U8aFixed from '../codec/U8aFixed'; @@ -13,8 +13,8 @@ import U8aFixed from '../codec/U8aFixed'; * as a sane default for fixed-length hash representations. */ export default class H256 extends U8aFixed implements IHash { - constructor (value?: AnyU8a) { - super(value, 256); + constructor (registry: Registry, value?: AnyU8a) { + super(registry, value, 256); } /** diff --git a/packages/types/src/primitive/H512.ts b/packages/types/src/primitive/H512.ts index 59fbf2ff4c79..732091ccb996 100644 --- a/packages/types/src/primitive/H512.ts +++ b/packages/types/src/primitive/H512.ts @@ -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 { AnyU8a } from '../types'; +import { AnyU8a, Registry } from '../types'; import U8aFixed from '../codec/U8aFixed'; @@ -12,8 +12,8 @@ import U8aFixed from '../codec/U8aFixed'; * Hash containing 512 bits (64 bytes), typically used for signatures */ export default class H512 extends U8aFixed { - constructor (value?: AnyU8a) { - super(value, 512); + constructor (registry: Registry, value?: AnyU8a) { + super(registry, value, 512); } /** diff --git a/packages/types/src/primitive/I128.ts b/packages/types/src/primitive/I128.ts index 90636f395bcd..f8593adfab67 100644 --- a/packages/types/src/primitive/I128.ts +++ b/packages/types/src/primitive/I128.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import Int from '../codec/Int'; @@ -12,7 +12,7 @@ import Int from '../codec/Int'; * A 128-bit signed integer */ export default class I128 extends Int { - constructor (value?: AnyNumber) { - super(value, 128); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 128); } } diff --git a/packages/types/src/primitive/I16.ts b/packages/types/src/primitive/I16.ts index 84333188b5ad..8c3ed259c897 100644 --- a/packages/types/src/primitive/I16.ts +++ b/packages/types/src/primitive/I16.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import Int from '../codec/Int'; @@ -12,7 +12,7 @@ import Int from '../codec/Int'; * A 16-bit signed integer */ export default class I16 extends Int { - constructor (value?: AnyNumber) { - super(value, 16); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 16); } } diff --git a/packages/types/src/primitive/I256.ts b/packages/types/src/primitive/I256.ts index d4f2aeeebe21..cbe9764e84da 100644 --- a/packages/types/src/primitive/I256.ts +++ b/packages/types/src/primitive/I256.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import Int from '../codec/Int'; @@ -12,7 +12,7 @@ import Int from '../codec/Int'; * A 256-bit signed integer */ export default class I256 extends Int { - constructor (value?: AnyNumber) { - super(value, 256); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 256); } } diff --git a/packages/types/src/primitive/I32.ts b/packages/types/src/primitive/I32.ts index e8ca0371fa41..58e082321296 100644 --- a/packages/types/src/primitive/I32.ts +++ b/packages/types/src/primitive/I32.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import Int from '../codec/Int'; @@ -12,7 +12,7 @@ import Int from '../codec/Int'; * A 32-bit signed integer */ export default class I32 extends Int { - constructor (value?: AnyNumber) { - super(value, 32); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 32); } } diff --git a/packages/types/src/primitive/I64.ts b/packages/types/src/primitive/I64.ts index 46d79742cd11..a6b006549b56 100644 --- a/packages/types/src/primitive/I64.ts +++ b/packages/types/src/primitive/I64.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import Int from '../codec/Int'; @@ -12,7 +12,7 @@ import Int from '../codec/Int'; * A 64-bit signed integer */ export default class I64 extends Int { - constructor (value?: AnyNumber) { - super(value, 64); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 64); } } diff --git a/packages/types/src/primitive/I8.ts b/packages/types/src/primitive/I8.ts index 575edfa2d02f..54ece4cdd50c 100644 --- a/packages/types/src/primitive/I8.ts +++ b/packages/types/src/primitive/I8.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import Int from '../codec/Int'; @@ -12,7 +12,7 @@ import Int from '../codec/Int'; * An 8-bit signed integer */ export default class I8 extends Int { - constructor (value?: AnyNumber) { - super(value, 8); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 8); } } diff --git a/packages/types/src/primitive/Null.spec.ts b/packages/types/src/primitive/Null.spec.ts index dd17ef3f7ef1..8de00da67aaa 100644 --- a/packages/types/src/primitive/Null.spec.ts +++ b/packages/types/src/primitive/Null.spec.ts @@ -2,18 +2,21 @@ // 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 '../codec'; import Null from './Null'; describe('Null', (): void => { + const registry = new TypeRegistry(); + it('compares against null', (): void => { - expect(new Null().eq(null)).toBe(true); + expect(new Null(registry).eq(null)).toBe(true); }); it('compares against Null', (): void => { - expect(new Null().eq(new Null())).toBe(true); + expect(new Null(registry).eq(new Null(registry))).toBe(true); }); it('compares against other (failed)', (): void => { - expect(new Null().eq()).toBe(false); + expect(new Null(registry).eq()).toBe(false); }); }); diff --git a/packages/types/src/primitive/Null.ts b/packages/types/src/primitive/Null.ts index f4bffa251e15..562339c00ab5 100644 --- a/packages/types/src/primitive/Null.ts +++ b/packages/types/src/primitive/Null.ts @@ -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 { Codec, IHash } from '../types'; +import { Codec, IHash, Registry } from '../types'; import { isNull } from '@polkadot/util'; @@ -12,6 +12,12 @@ import { isNull } from '@polkadot/util'; * Implements a type that does not contain anything (apart from `null`) */ export default class Null implements Codec { + public readonly registry: Registry; + + constructor (registry: Registry) { + this.registry = registry; + } + /** * @description The length of the value when encoded as a Uint8Array */ diff --git a/packages/types/src/primitive/StorageHasher.ts b/packages/types/src/primitive/StorageHasher.ts index 1a9a0196cc80..8c0bf750d344 100644 --- a/packages/types/src/primitive/StorageHasher.ts +++ b/packages/types/src/primitive/StorageHasher.ts @@ -2,11 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '../types'; + import Enum from '../codec/Enum'; export default class StorageHasher extends Enum { - constructor (value?: any) { - super([ + constructor (registry: Registry, value?: any) { + super(registry, [ 'Blake2_128', 'Blake2_256', 'Twox128', diff --git a/packages/types/src/primitive/StorageKey.spec.ts b/packages/types/src/primitive/StorageKey.spec.ts index 85d2b700bfe7..4c744e43cb21 100644 --- a/packages/types/src/primitive/StorageKey.spec.ts +++ b/packages/types/src/primitive/StorageKey.spec.ts @@ -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 '../injector'; - import Metadata from '@polkadot/metadata'; import rpcDataV3 from '@polkadot/metadata/Metadata/v3/static'; import rpcDataV4 from '@polkadot/metadata/Metadata/v4/static'; @@ -13,15 +11,19 @@ import rpcDataV7 from '@polkadot/metadata/Metadata/v7/static'; import rpcDataV8 from '@polkadot/metadata/Metadata/v8/static'; import { hexToU8a } from '@polkadot/util'; +import { TypeRegistry } from '../codec'; import StorageKey from './StorageKey'; describe('StorageKey', (): void => { + const registry = new TypeRegistry(); + describe('with MetadataV3 (uses xxHash by default)', (): void => { - const metadata = new Metadata(rpcDataV3); + const metadata = new Metadata(registry, rpcDataV3); it('should correctly get Alice\'s freeBalance storage key (hex)', (): void => { expect( new StorageKey( + registry, metadata .query .balances @@ -34,6 +36,7 @@ describe('StorageKey', (): void => { it('should correctly get Alice\'s freeBalance storage key (u8a)', (): void => { expect( new StorageKey( + registry, metadata .query .balances @@ -48,11 +51,12 @@ describe('StorageKey', (): void => { }); describe('with MetadataV4 (uses xxHash by default)', (): void => { - const metadata = new Metadata(rpcDataV4); + const metadata = new Metadata(registry, rpcDataV4); it('should correctly get Alice\'s freeBalance storage key (hex)', (): void => { expect( new StorageKey( + registry, metadata .query .balances @@ -65,6 +69,7 @@ describe('StorageKey', (): void => { it('should correctly get Alice\'s freeBalance storage key (u8a)', (): void => { expect( new StorageKey( + registry, metadata .query .balances @@ -81,7 +86,7 @@ describe('StorageKey', (): void => { it('should correctly get the EventTopics double map storage key (hex)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -94,7 +99,7 @@ describe('StorageKey', (): void => { it('should correctly get the EventTopics double map storage key (u8a)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -107,11 +112,11 @@ describe('StorageKey', (): void => { }); describe('with MetadataV5', (): void => { - const metadata = new Metadata(rpcDataV5); + const metadata = new Metadata(registry, rpcDataV5); it('should correctly get the EventTopics double map storage key (hex)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -124,7 +129,7 @@ describe('StorageKey', (): void => { it('should correctly get the EventTopics double map storage key (u8a)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -137,11 +142,11 @@ describe('StorageKey', (): void => { }); describe('with MetadataV6', (): void => { - const metadata = new Metadata(rpcDataV6); + const metadata = new Metadata(registry, rpcDataV6); it('should correctly get the EventTopics double map storage key (hex)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -154,7 +159,7 @@ describe('StorageKey', (): void => { it('should correctly get the EventTopics double map storage key (u8a)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -167,11 +172,11 @@ describe('StorageKey', (): void => { }); describe('with MetadataV7', (): void => { - const metadata = new Metadata(rpcDataV7); + const metadata = new Metadata(registry, rpcDataV7); it('should correctly get the EventTopics double map storage key (hex)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -184,7 +189,7 @@ describe('StorageKey', (): void => { it('should correctly get the EventTopics double map storage key (u8a)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -197,11 +202,11 @@ describe('StorageKey', (): void => { }); describe('with MetadataV8', (): void => { - const metadata = new Metadata(rpcDataV8); + const metadata = new Metadata(registry, rpcDataV8); it('should correctly get the EventTopics double map storage key (hex)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system @@ -214,7 +219,7 @@ describe('StorageKey', (): void => { it('should correctly get the EventTopics double map storage key (u8a)', (): void => { expect( - new StorageKey([ + new StorageKey(registry, [ metadata .query .system diff --git a/packages/types/src/primitive/StorageKey.ts b/packages/types/src/primitive/StorageKey.ts index 08a0d7e207e7..7496c01f4e73 100644 --- a/packages/types/src/primitive/StorageKey.ts +++ b/packages/types/src/primitive/StorageKey.ts @@ -2,10 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { AnyU8a, Registry } from '../types'; + import { StorageEntryMetadata as MetaV8 } from '@polkadot/metadata/Metadata/v8/Storage'; import { assert, isFunction, isString, isU8a } from '@polkadot/util'; -import { AnyU8a } from '../types'; import Bytes from './Bytes'; export interface StorageEntry { @@ -44,10 +45,10 @@ export default class StorageKey extends Bytes { private _section?: string; - constructor (value?: AnyU8a | StorageKey | StorageEntry | [StorageEntry, any], override: Partial = {}) { + constructor (registry: Registry, value?: AnyU8a | StorageKey | StorageEntry | [StorageEntry, any], override: Partial = {}) { const { key, method, section } = StorageKey.decodeStorageKey(value); - super(key); + super(registry, key); this._meta = StorageKey.getMeta(value as StorageKey); this._method = override.method || method; diff --git a/packages/types/src/primitive/Text.spec.ts b/packages/types/src/primitive/Text.spec.ts index 0a6ea65c79de..b4fcdb1d225b 100644 --- a/packages/types/src/primitive/Text.spec.ts +++ b/packages/types/src/primitive/Text.spec.ts @@ -3,28 +3,31 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { CodecTo } from '../types'; +import { TypeRegistry } from '../codec/create'; import U8a from '../codec/U8a'; import Text from './Text'; describe('Text', (): void => { + const registry = new TypeRegistry(); + describe('decode', (): void => { const testDecode = (type: string, input: string | Uint8Array | { toString: () => string }, expected: string, toFn: 'toString' | 'toHex' = 'toString'): void => it(`can decode from ${type}`, (): void => { - expect(new Text(input)[toFn]()).toBe(expected); + expect(new Text(registry, input)[toFn]()).toBe(expected); }); testDecode('string', 'foo', 'foo'); - testDecode('Text', new Text('foo'), 'foo'); + testDecode('Text', new Text(registry, 'foo'), 'foo'); testDecode('Uint8Array', Uint8Array.from([12, 102, 111, 111]), 'foo'); - testDecode('U8a', new U8a(Uint8Array.from([12, 102, 111, 111])), 'foo'); + testDecode('U8a', new U8a(registry, Uint8Array.from([12, 102, 111, 111])), 'foo'); testDecode('object with `toString()`', { toString (): string { return 'foo'; } }, 'foo'); - testDecode('hex input value', new Text('0x12345678'), '0x12345678', 'toHex'); + testDecode('hex input value', new Text(registry, '0x12345678'), '0x12345678', 'toHex'); }); describe('encode', (): void => { const testEncode = (to: CodecTo, expected: string | Uint8Array): void => it(`can encode ${to}`, (): void => { - expect(new Text('foo')[to]()).toEqual(expected); + expect(new Text(registry, 'foo')[to]()).toEqual(expected); }); testEncode('toHex', '0x666f6f'); @@ -34,26 +37,26 @@ describe('Text', (): void => { describe('utils', (): void => { it('compares actual string values', (): void => { - expect(new Text('123').eq('123')).toBe(true); + expect(new Text(registry, '123').eq('123')).toBe(true); }); it('compares actual String values', (): void => { - expect(new Text('XYX').eq(String('XYX'))).toBe(true); + expect(new Text(registry, 'XYX').eq(String('XYX'))).toBe(true); }); it('compares actual non-string values (fails)', (): void => { - expect(new Text('123').eq(123)).toBe(false); + expect(new Text(registry, '123').eq(123)).toBe(false); }); it('calulates the length & encoded length correctly for ASCII', (): void => { - const test = new Text('abcde'); + const test = new Text(registry, 'abcde'); expect(test.encodedLength).toEqual(6); expect(test).toHaveLength(5); }); it('calulates the length & encoded length correctly for non-ASCII', (): void => { - const test = new Text('中文'); + const test = new Text(registry, '中文'); expect(test.encodedLength).toEqual(7); expect(test).toHaveLength(2); diff --git a/packages/types/src/primitive/Text.ts b/packages/types/src/primitive/Text.ts index e8ee497e1677..bd077845d9da 100644 --- a/packages/types/src/primitive/Text.ts +++ b/packages/types/src/primitive/Text.ts @@ -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 { AnyU8a, Codec, IHash } from '../types'; +import { AnyU8a, Codec, IHash, Registry } from '../types'; import { assert, hexToU8a, isHex, isString, stringToU8a, u8aToString, u8aToHex } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -21,10 +21,12 @@ import Compact from '../codec/Compact'; // TODO // - Strings should probably be trimmed (docs do come through with extra padding) export default class Text extends String implements Codec { - constructor (value: Text | string | AnyU8a | { toString: () => string } = '') { - super( - Text.decodeText(value) - ); + public readonly registry: Registry; + + constructor (registry: Registry, value: Text | string | AnyU8a | { toString: () => string } = '') { + super(Text.decodeText(value)); + + this.registry = registry; } private static decodeText (value: Text | string | AnyU8a | { toString: () => string }): string { @@ -57,7 +59,7 @@ export default class Text extends String implements Codec { * @description returns a hash of the contents */ public get hash (): IHash { - return createType('Hash', blake2AsU8a(this.toU8a(), 256)); + return createType(this.registry, 'Hash', blake2AsU8a(this.toU8a(), 256)); } /** diff --git a/packages/types/src/primitive/Type.spec.ts b/packages/types/src/primitive/Type.spec.ts index e0c3ff1df4bf..77bc834ca92c 100644 --- a/packages/types/src/primitive/Type.spec.ts +++ b/packages/types/src/primitive/Type.spec.ts @@ -4,94 +4,98 @@ import { stringToU8a, u8aConcat } from '@polkadot/util'; +import { TypeRegistry } from '../codec'; import Text from './Text'; import Type from './Type'; describe('Type', (): void => { + const registry = new TypeRegistry(); + it('fails to cleanup invalid boxes', (): void => { expect( - (): Type => new Type('Box new Type(registry, 'Box { expect( - new Type('(AccountId)').toString() + new Type(registry, '(AccountId)').toString() ).toEqual('AccountId'); }); it('does not touch tuples with multiple values', (): void => { expect( - new Type('(AccountId, Balance)').toString() + new Type(registry, '(AccountId, Balance)').toString() ).toEqual('(AccountId,Balance)'); }); it('handles nested types', (): void => { expect( - new Type('Box>').toString() + new Type(registry, 'Box>').toString() ).toEqual('Vec'); }); it('handles nested types (embedded)', (): void => { expect( - new Type('(u32, Box>)').toString() + new Type(registry, '(u32, Box>)').toString() ).toEqual('(u32,Vec)'); }); it('handles aliasses, multiples per line', (): void => { expect( - new Type('(Vec, AccountId, Vec)').toString() + new Type(registry, '(Vec, AccountId, Vec)').toString() ).toEqual('(Bytes,AccountId,Bytes)'); }); it('removes whitespaces', (): void => { expect( - new Type('T :: AccountId').toString() + new Type(registry, 'T :: AccountId').toString() ).toEqual('AccountId'); }); it('changes PairOf -> (T, T)', (): void => { expect( - new Type('PairOf').toString() + new Type(registry, 'PairOf').toString() ).toEqual('(Balance,Balance)'); }); it('changes PairOf (embedded) -> (T, T)', (): void => { expect( - new Type('(Vec, PairOf, Vec)').toString() + new Type(registry, '(Vec, PairOf, Vec)').toString() ).toEqual('(Bytes,(Balance,Balance),Vec)'); }); it('changes () -> Null', (): void => { expect( - new Type('()').toString() + new Type(registry, '()').toString() ).toEqual('Null'); }); it('has a length for the type', (): void => { expect( new Type( - new Text(' Box ') + registry, + new Text(registry, ' Box ') ).toString() ).toEqual('Proposal'); // eslint-disable-line }); it('unwraps compact', (): void => { expect( - new Type('::Type').toString() + new Type(registry, '::Type').toString() ).toEqual('Compact'); }); it('handles InherentOfflineReport', (): void => { expect( - new Type('::Inherent').toString() + new Type(registry, '::Inherent').toString() ).toEqual('InherentOfflineReport'); }); it('encodes correctly via toU8a()', (): void => { const type = 'Compact'; - expect(new Text(type).toU8a()).toEqual( + expect(new Text(registry, type).toU8a()).toEqual( u8aConcat( new Uint8Array([type.length << 2]), stringToU8a(type) diff --git a/packages/types/src/primitive/Type.ts b/packages/types/src/primitive/Type.ts index 9e035052e0c4..5dcd51d68098 100644 --- a/packages/types/src/primitive/Type.ts +++ b/packages/types/src/primitive/Type.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '../types'; + import sanitize from '../codec/create/sanitize'; import Text from './Text'; @@ -10,22 +12,18 @@ import Text from './Text'; * @description * This is a extended version of String, specifically to handle types. Here we rely fully * on what string provides us, however we also adjust the types received from the runtime, - * i.e. we remove the `T::` prefixes found in some types for consistency accross implementation. + * i.e. we remove the `T::` prefixes found in some types for consistency across implementation. */ export default class Type extends Text { private _originalLength: number; - constructor (value: Text | Uint8Array | string = '') { + constructor (registry: Registry, value: Text | Uint8Array | string = '') { // First decode it with Text - const textValue = new Text(value); + const textValue = new Text(registry, value); // Then cleanup the textValue to get the @polkadot/types type, and pass the // sanitized value to constructor - super( - sanitize( - textValue.toString() - ) - ); + super(registry, sanitize(textValue.toString())); this._originalLength = textValue.encodedLength; } diff --git a/packages/types/src/primitive/U128.ts b/packages/types/src/primitive/U128.ts index ec11a8f0e0bc..a499be27b07f 100644 --- a/packages/types/src/primitive/U128.ts +++ b/packages/types/src/primitive/U128.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import UInt from '../codec/UInt'; @@ -12,7 +12,7 @@ import UInt from '../codec/UInt'; * A 128-bit unsigned integer */ export default class U128 extends UInt { - constructor (value?: AnyNumber) { - super(value, 128); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 128); } } diff --git a/packages/types/src/primitive/U16.ts b/packages/types/src/primitive/U16.ts index ac526a125362..08e9f9109d15 100644 --- a/packages/types/src/primitive/U16.ts +++ b/packages/types/src/primitive/U16.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import UInt from '../codec/UInt'; @@ -12,7 +12,7 @@ import UInt from '../codec/UInt'; * A 16-bit unsigned integer */ export default class U16 extends UInt { - constructor (value?: AnyNumber) { - super(value, 16); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 16); } } diff --git a/packages/types/src/primitive/U256.ts b/packages/types/src/primitive/U256.ts index 88daf066de7a..e9a5287b6945 100644 --- a/packages/types/src/primitive/U256.ts +++ b/packages/types/src/primitive/U256.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import UInt from '../codec/UInt'; @@ -12,7 +12,7 @@ import UInt from '../codec/UInt'; * A 256-bit unsigned integer */ export default class U256 extends UInt { - constructor (value?: AnyNumber) { - super(value, 256); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 256); } } diff --git a/packages/types/src/primitive/U32.ts b/packages/types/src/primitive/U32.ts index 8c8976c2eb9d..7c897a8a032e 100644 --- a/packages/types/src/primitive/U32.ts +++ b/packages/types/src/primitive/U32.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import UInt from '../codec/UInt'; @@ -12,7 +12,7 @@ import UInt from '../codec/UInt'; * A 32-bit unsigned integer */ export default class U32 extends UInt { - constructor (value?: AnyNumber) { - super(value, 32); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 32); } } diff --git a/packages/types/src/primitive/U64.ts b/packages/types/src/primitive/U64.ts index cece8eb05d19..78701341c02f 100644 --- a/packages/types/src/primitive/U64.ts +++ b/packages/types/src/primitive/U64.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import UInt from '../codec/UInt'; @@ -12,7 +12,7 @@ import UInt from '../codec/UInt'; * A 64-bit unsigned integer */ export default class U64 extends UInt { - constructor (value?: AnyNumber) { - super(value, 64); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 64); } } diff --git a/packages/types/src/primitive/U8.ts b/packages/types/src/primitive/U8.ts index 02b9efaaf0ab..e8f57bf864fd 100644 --- a/packages/types/src/primitive/U8.ts +++ b/packages/types/src/primitive/U8.ts @@ -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 { AnyNumber } from '../types'; +import { AnyNumber, Registry } from '../types'; import UInt from '../codec/UInt'; @@ -12,7 +12,7 @@ import UInt from '../codec/UInt'; * An 8-bit unsigned integer */ export default class U8 extends UInt { - constructor (value?: AnyNumber) { - super(value, 8); + constructor (registry: Registry, value?: AnyNumber) { + super(registry, value, 8); } } diff --git a/packages/types/src/primitive/USize.ts b/packages/types/src/primitive/USize.ts index 4e6390e91f5f..f814ca525174 100644 --- a/packages/types/src/primitive/USize.ts +++ b/packages/types/src/primitive/USize.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '../types'; + import U32 from './U32'; /** @@ -12,8 +14,8 @@ import U32 from './U32'; * It is not to be used, since it created consensus mismatches. */ export default class USize extends U32 { - constructor (value?: any) { - super(value); + constructor (registry: Registry, value?: any) { + super(registry, value); throw new Error('The `usize` type should not be used. Since it is platform-specific, it creates incompatibilities between native (generally u64) and WASM (always u32) code. Use one of the `u32` or `u64` types explicitly.'); } diff --git a/packages/types/src/primitive/Unconstructable.ts b/packages/types/src/primitive/Unconstructable.ts index 275b192933ce..b9516b4d6088 100644 --- a/packages/types/src/primitive/Unconstructable.ts +++ b/packages/types/src/primitive/Unconstructable.ts @@ -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 { Constructor } from '../types'; +import { Constructor, Registry } from '../types'; import Null from './Null'; @@ -12,8 +12,8 @@ import Null from './Null'; * A type that should not be constructed */ export default class Unconstructable extends Null { - constructor () { - super(); + constructor (registry: Registry) { + super(registry); throw new Error('Unconstructable should not be constructed, it is only a placeholder for compatibility'); } diff --git a/packages/types/src/scripts/MetadataMd.ts b/packages/types/src/scripts/MetadataMd.ts index bf813e0b716e..76b8cfd2763b 100644 --- a/packages/types/src/scripts/MetadataMd.ts +++ b/packages/types/src/scripts/MetadataMd.ts @@ -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 '../injector'; - import fs from 'fs'; import Metadata from '@polkadot/metadata'; import rpcdata from '@polkadot/metadata/Metadata/static'; @@ -12,6 +10,7 @@ import { stringCamelCase, stringLowerFirst } from '@polkadot/util'; import interfaces from '../../../type-jsonrpc/src'; import Call from '../primitive/Generic/Call'; +import { TypeRegistry } from '../codec'; const ANCHOR_TOP = ''; const LINK_BACK_TO_TOP = ''; @@ -237,7 +236,8 @@ function writeToEventsMd (metadata: MetadataV8): void { writeFile('docs/substrate/events.md', addEvents(metadata)); } -const metadata = new Metadata(rpcdata).metadata.asLatest; +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcdata).metadata.asLatest; writeToRpcMd(); writeToConstantsMd(metadata); diff --git a/packages/types/src/scripts/generateTypes/consts.ts b/packages/types/src/scripts/generateTypes/consts.ts index 1a6c3240041e..46bd69d2c98f 100644 --- a/packages/types/src/scripts/generateTypes/consts.ts +++ b/packages/types/src/scripts/generateTypes/consts.ts @@ -7,7 +7,7 @@ import { ModuleMetadataV8 } from '@polkadot/metadata/Metadata/v8/Metadata'; import staticData from '@polkadot/metadata/Metadata/static'; import { stringCamelCase } from '@polkadot/util'; -import { Metadata } from '../..'; +import { Metadata, TypeRegistry } from '../..'; import { createImportCode, createImports, FOOTER, HEADER, indent, setImports, TypeImports } from '../util'; // Generate types for one module @@ -36,9 +36,6 @@ function generateModule (modul: ModuleMetadataV8, imports: TypeImports): string[ function generateForMeta (meta: Metadata): void { console.log('Writing packages/api/src/consts.types.ts'); - // Inject all types so that metadata can use them - require('../../injector'); - const imports = createImports(); // Will hold all needed imports const body = meta.asLatest.modules.reduce((acc, modul): string[] => { @@ -86,5 +83,7 @@ function generateForMeta (meta: Metadata): void { // Call `generateForMeta()` with current static metadata export default function generateConsts (): void { - return generateForMeta(new Metadata(staticData)); + const registry = new TypeRegistry(); + + return generateForMeta(new Metadata(registry, staticData)); } diff --git a/packages/types/src/scripts/generateTypes/query.ts b/packages/types/src/scripts/generateTypes/query.ts index 1e802b6f2ce6..ca5a77b37b3e 100644 --- a/packages/types/src/scripts/generateTypes/query.ts +++ b/packages/types/src/scripts/generateTypes/query.ts @@ -2,6 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Registry } from '@polkadot/types/types'; + import fs from 'fs'; import { ModuleMetadataV8 } from '@polkadot/metadata/Metadata/v8/Metadata'; import { StorageEntryMetadata } from '@polkadot/metadata/Metadata/v8/Storage'; @@ -10,16 +12,17 @@ import { stringLowerFirst } from '@polkadot/util'; import { Metadata } from '../..'; import { createImportCode, createImports, FOOTER, formatType, getSimilarTypes, HEADER, indent, setImports, TypeImports } from '../util'; +import { TypeRegistry } from '@polkadot/types/codec'; // From a storage entry metadata, we return [args, returnType] -function entrySignature (storageEntry: StorageEntryMetadata, imports: TypeImports): [string, string] { +function entrySignature (registry: Registry, storageEntry: StorageEntryMetadata, imports: TypeImports): [string, string] { if (storageEntry.type.isPlainType) { setImports(imports, [storageEntry.type.asType.toString()]); return ['', formatType(storageEntry.type.asType.toString(), imports)]; } else if (storageEntry.type.isMap) { // Find similar types of the `key` type - const similarTypes = getSimilarTypes(storageEntry.type.asMap.key.toString(), imports); + const similarTypes = getSimilarTypes(registry, storageEntry.type.asMap.key.toString(), imports); setImports(imports, [ ...similarTypes, @@ -32,8 +35,8 @@ function entrySignature (storageEntry: StorageEntryMetadata, imports: TypeImport ]; } else if (storageEntry.type.isDoubleMap) { // Find similartypes of `key1` and `key2` types - const similarTypes1 = getSimilarTypes(storageEntry.type.asDoubleMap.key1.toString(), imports); - const similarTypes2 = getSimilarTypes(storageEntry.type.asDoubleMap.key2.toString(), imports); + const similarTypes1 = getSimilarTypes(registry, storageEntry.type.asDoubleMap.key1.toString(), imports); + const similarTypes2 = getSimilarTypes(registry, storageEntry.type.asDoubleMap.key2.toString(), imports); setImports(imports, [ ...similarTypes1, @@ -54,8 +57,8 @@ function entrySignature (storageEntry: StorageEntryMetadata, imports: TypeImport } // Generate types for one storage entry in a module -function generateEntry (storageEntry: StorageEntryMetadata, imports: TypeImports): string[] { - const [args, returnType] = entrySignature(storageEntry, imports); +function generateEntry (registry: Registry, storageEntry: StorageEntryMetadata, imports: TypeImports): string[] { + const [args, returnType] = entrySignature(registry, storageEntry, imports); return [ `${stringLowerFirst(storageEntry.name.toString())}: StorageEntryExact Observable<${returnType}>> & QueryableStorageEntry;` @@ -63,7 +66,7 @@ function generateEntry (storageEntry: StorageEntryMetadata, imports: TypeImports } // Generate types for one module -function generateModule (modul: ModuleMetadataV8, imports: TypeImports): string[] { +function generateModule (registry: Registry, modul: ModuleMetadataV8, imports: TypeImports): string[] { if (modul.storage.isNone) { return []; } @@ -73,7 +76,7 @@ function generateModule (modul: ModuleMetadataV8, imports: TypeImports): string[ .concat( modul.storage.unwrap().items .reduce((acc, storageEntry): string[] => { - return acc.concat(generateEntry(storageEntry, imports).map(indent(6))); + return acc.concat(generateEntry(registry, storageEntry, imports).map(indent(6))); }, [] as string[]) .join('\n') ) @@ -82,16 +85,13 @@ function generateModule (modul: ModuleMetadataV8, imports: TypeImports): string[ // Generate `packages/types-jsonrpc/src/jsonrpc.types.ts` for a particular // metadata -function generateForMeta (meta: Metadata): void { +function generateForMeta (registry: Registry, meta: Metadata): void { console.log('Writing packages/api/src/query.types.ts'); - // Inject all types so that metadata can use them - require('../../injector'); - const imports = createImports(); // Will hold all needed imports const body = meta.asLatest.modules.reduce((acc, modul): string[] => { - const storageEntries = generateModule(modul, imports); + const storageEntries = generateModule(registry, modul, imports); return acc.concat(storageEntries); }, [] as string[]); @@ -139,5 +139,7 @@ function generateForMeta (meta: Metadata): void { // Call `generateForMeta()` with current static metadata export default function generateQuery (): void { - return generateForMeta(new Metadata(staticData)); + const registry = new TypeRegistry(); + + return generateForMeta(registry, new Metadata(registry, staticData)); } diff --git a/packages/types/src/scripts/generateTypes/rpc.ts b/packages/types/src/scripts/generateTypes/rpc.ts index 3ed605898058..d536ad076cad 100644 --- a/packages/types/src/scripts/generateTypes/rpc.ts +++ b/packages/types/src/scripts/generateTypes/rpc.ts @@ -5,15 +5,14 @@ import fs from 'fs'; import interfaces from '../../../../type-jsonrpc/src'; +import { TypeRegistry } from '../../codec/create'; import { createImportCode, createImports, FOOTER, getSimilarTypes, HEADER, setImports } from '../util'; // Generate `packages/types-jsonrpc/src/jsonrpc.types.ts` export default function generateRpcTypes (): void { console.log('Writing packages/rpc-core/jsonrpc.types.ts'); - // Inject all types so that types-jsonrpc can use them - require('../../injector'); - + const registry = new TypeRegistry(); const imports = createImports(); const body = Object.keys(interfaces).sort().reduce((allSections, section): string[] => { @@ -31,7 +30,7 @@ export default function generateRpcTypes (): void { } const args = method.params.map((param): string => { - const similarTypes = getSimilarTypes(param.type, imports); + const similarTypes = getSimilarTypes(registry, param.type, imports); setImports(imports, [param.type, ...similarTypes]); return `${param.name}${param.isOptional ? '?' : ''}: ${similarTypes.join(' | ')}`; diff --git a/packages/types/src/scripts/util/derived.ts b/packages/types/src/scripts/util/derived.ts index 821d3b9a9dfd..0399a4b628e0 100644 --- a/packages/types/src/scripts/util/derived.ts +++ b/packages/types/src/scripts/util/derived.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { TypeDef } from '../../codec/types'; -import { Constructor } from '../../types'; +import { Constructor, Registry } from '../../types'; import { isChildClass, isCompactEncodable } from './class'; import { ClassOfUnsafe, getTypeDef } from '../../codec/create'; @@ -34,7 +34,7 @@ export function getDerivedTypes (type: string, primitiveName: string, imports: T // Make types a little bit more flexible // - if param instanceof AbstractInt, then param: u64 | Uint8array | string | number // etc -export function getSimilarTypes (type: string, imports: TypeImports): string[] { +export function getSimilarTypes (registry: Registry, type: string, imports: TypeImports): string[] { const possibleTypes = [type]; if (type === 'Extrinsic') { @@ -42,8 +42,8 @@ export function getSimilarTypes (type: string, imports: TypeImports): string[] { return ['IExtrinsic']; } - if (isChildClass(Vec, ClassOfUnsafe(type))) { - return [`(${getSimilarTypes(((getTypeDef(type).sub) as TypeDef).type, imports).join(' | ')})[]`]; + if (isChildClass(Vec, ClassOfUnsafe(registry, type))) { + return [`(${getSimilarTypes(registry, ((getTypeDef(type).sub) as TypeDef).type, imports).join(' | ')})[]`]; } // FIXME This is a hack, it's hard to correctly type StorageKeys in the @@ -53,11 +53,11 @@ export function getSimilarTypes (type: string, imports: TypeImports): string[] { } // Cannot get isChildClass of abstract class, but it works - if (isChildClass(AbstractInt as unknown as Constructor, ClassOfUnsafe(type))) { + if (isChildClass(AbstractInt as unknown as Constructor, ClassOfUnsafe(registry, type))) { possibleTypes.push('Uint8Array', 'number', 'string'); - } else if (isChildClass(Uint8Array, ClassOfUnsafe(type))) { + } else if (isChildClass(Uint8Array, ClassOfUnsafe(registry, type))) { possibleTypes.push('Uint8Array', 'string'); - } else if (isChildClass(String, ClassOfUnsafe(type))) { + } else if (isChildClass(String, ClassOfUnsafe(registry, type))) { possibleTypes.push('string'); } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 8aa02784d595..6d5cd9a3e6b2 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -67,24 +67,29 @@ export type AnyJson = string | number | boolean | null | undefined | AnyJsonObje * @name Codec * @description * The base Codec interface. All types implement the interface provided here. Additionally - * implementors can add their own specific interfaces and helpres with getters and functions. + * implementors can add their own specific interfaces and helpers with getters and functions. * The Codec Base is however required for operating as an encoding/decoding layer */ export interface Codec { /** * @description The length of the value when encoded as a Uint8Array */ - encodedLength: number; + readonly encodedLength: number; /** * @description Returns a hash of the value */ - hash: IHash; + readonly hash: IHash; /** * @description Checks if the value is an empty value */ - isEmpty: boolean; + readonly isEmpty: boolean; + + /** + * @description The registry associated with this object + */ + readonly registry: Registry; /** * @description Compares the value of the input to see if there is a match @@ -125,7 +130,7 @@ export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a'; export interface Constructor { // eslint-disable-next-line @typescript-eslint/no-explicit-any - new(...value: any[]): T; + new(registry: Registry, ...value: any[]): T; } export type ConstructorDef = Record>; @@ -304,3 +309,60 @@ export interface ISignerPayload { toPayload (): SignerPayloadJSON; toRaw (): SignerPayloadRaw; } + +export interface RegistryMetadataCall { + args: any[]; + // eslint-disable-next-line @typescript-eslint/ban-types + name: String & Codec; + + toJSON (): string | AnyJsonObject; +} + +export interface RegistryMetadataCalls { + isSome: boolean; + unwrap (): RegistryMetadataCall[]; +} + +export interface RegistryMetadataEvent { + args: any[]; + // eslint-disable-next-line @typescript-eslint/ban-types + name: String & Codec; +} + +export interface RegistryMetadataEvents { + isSome: boolean; + unwrap (): RegistryMetadataEvent[]; +} + +export interface RegistryMetadataModule { + calls: RegistryMetadataCalls; + events: RegistryMetadataEvents; + // eslint-disable-next-line @typescript-eslint/ban-types + name: String & Codec; +} + +export interface RegistryMetadataLatest { + modules: RegistryMetadataModule[]; +} + +export interface RegistryMetadata { + asLatest: RegistryMetadataLatest; +} + +export interface Registry { + findMetaCall (callIndex: Uint8Array): CallFunction; + + // due to same circular imports where types don't really want to import from EventData, + // keep this as a generic Codec, however the actual impl. returns the correct + findMetaEvent (eventIndex: Uint8Array): Constructor; + + get (name: string): Constructor | undefined; + getOrThrow (name: string, msg?: string): Constructor; + hasClass (name: string): boolean; + hasDef (name: string): boolean; + hasType (name: string): boolean; + register (type: Constructor | RegistryTypes): void; + register (name: string, type: Constructor): void; + register (arg1: string | Constructor | RegistryTypes, arg2?: Constructor): void; + setMetadata (metadata: RegistryMetadata): void; +}