This commit is contained in:
ian.he
2019-04-11 13:42:06 +12:00
commit 4e30fab9d6
688 changed files with 51707 additions and 0 deletions
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+23
View File
@@ -0,0 +1,23 @@
# @polkadot/types
Implementation of the types and their (de-)serialisation via [parity-codec](https://github.com/paritytech/parity-codec).
## Primitive types
There primitive types are available: [[Bool]], [[Bytes]], [[Data]], [[H160]], [[H256]], [[H512]], [[I8]], [[I16]], [[I32]], [[I64]], [[I128]], [[I256]], [[Method]], [[Moment]], [[Null]], [[StorageData]], [[StorageKey]], [[Text]], [[Type]], [[U8]], [[U16]], [[U32]], [[U64]], [[U128]], [[U256]], [[USize]]
## Substrate types
These types implement specific types that are found as part of the Substrate base: [[AccountId]], [[AccountIndex]], [[Address]], [[Amount]], [[AssetOf]], [[AttestedCandidate]], [[AuthorityId]], [[Balance]], [[BalanceLock]], [[BalanceOf]], [[Block]], [[BlockNumber]], [[CodeHash]], [[Digest]], [[Event]], [[EventRecord]], [[Exposure]], [[Extrinsic]], [[ExtrinsicEra]], [[ExtrinsicSignature]], [[Gas]], [[Hash]], [[Header]], [[IndividualExposure]], [[InherentOfflineReport]], [[Justification]], [[Key]], [[KeyValue]], [[LockIdentifier]], [[MisbehaviorReport]], [[NewAccountOutcome]], [[Nonce]], [[Origin]], [[ParaId]], [[Perbill]], [[Permill]], [[PrefabWasmModule]], [[PropIndex]], [[Proposal]], [[ProposalIndex]], [[ReferendumIndex]], [[ReferendumInfo]], [[RewardDestination]], [[Schedule]], [[SeedOf]], [[SessionKey]], [[Signature]], [[SignaturePayload]], [[StakingLedger]], [[StoredPendingChange]], [[UnlockChunk]], [[ValidatorPrefs]], [[VestingSchedule]], [[Vote]], [[VoteIndex]], [[VoteThreshold]], [[WithdrawReasons]]
## Codec types
These are the base types of the codec, typically not used directly, but rather inherited from to create specific types: [[Base]], [[Compact]], [[Enum]], [[EnumType]], [[Option]], [[Set]], [[Struct]], [[Tuple]], [[U8a]], [[U8aFixed]], [[UInt]], [[Vector]]
## RPC types
These types are not used in the runtime, but rather are used in RPC results: [[ChainProperties]], [[ExtrinsicStatus]], [[Health]], [[Json]], [[NetworkState]], [[Metadata]], [[PeerInfo]], [[PendingExtrinsics]], [[RuntimeVersion]], [[SignedBlock]], [[StorageChangeSet]]
## Derived types
Some types are extended from the base to provide additional information: [[HeaderExtended]], [[ReferendumInfoExtended]]
+36
View File
@@ -0,0 +1,36 @@
{
"name": "@plugnet/types",
"version": "0.76.1",
"description": "Implementation of the Parity codec",
"main": "index.js",
"keywords": [
"Polkadot",
"JsonRPC"
],
"author": "Jaco Greeff <jacogr@gmail.com>",
"maintainers": [
"Jaco Greeff <jacogr@gmail.com>"
],
"contributors": [],
"license": "Apache-2.0",
"engines": {
"node": ">=6.4"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"repository": {
"type": "git",
"url": "git+https://github.com/polkadot-js/api.git"
},
"bugs": {
"url": "https://github.com/polkadot-js/api/issues"
},
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/types#readme",
"dependencies": {
"@babel/runtime": "^7.4.3",
"@plugnet/keyring": "^0.76.1",
"@plugnet/util": "^0.76.1"
}
}
+148
View File
@@ -0,0 +1,148 @@
// 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 { CodecArg, Constructor } from './types';
import { assert, isNumber, isNull, isString, isUndefined, stringCamelCase } from '@plugnet/util';
import Compact from './codec/Compact';
import { createClass } from './codec/createType';
export type ContractABIArgs = Array<{
name: string,
type: string
}>;
export type ContractABIMethodBase = {
args: ContractABIArgs
};
export type ContractABIMethod = ContractABIMethodBase & {
mutates?: boolean,
name: string,
selector: number,
return_type: string | null
};
export type ContractABI = {
deploy: ContractABIMethodBase,
messages: Array<ContractABIMethod>,
name: string
};
export interface ContractABIFn {
(...args: Array<CodecArg>): Uint8Array;
args: ContractABIArgs;
isConstant: boolean;
type: string | null;
}
export interface Contract {
abi: ContractABI;
deploy: ContractABIFn;
messages: {
[index: string]: ContractABIFn
};
}
export function validateArgs (name: string, args: ContractABIArgs): void {
assert(Array.isArray(args), `Expected 'args' to exist on ${name}`);
args.forEach((arg) => {
const unknownKeys = Object.keys(arg).filter((key) => !['name', 'type'].includes(key));
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for ${name}`);
assert(isString(arg.name) && isString(arg.type), `${name} args should have valid name & type values`);
});
}
export function validateDeploy ({ deploy }: ContractABI): void {
const unknownKeys = Object.keys(deploy).filter((key) => !['args'].includes(key));
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI deploy`);
validateArgs('deploy', deploy.args);
}
export function validateMethods ({ messages }: ContractABI): void {
messages.forEach((method) => {
const unknownKeys = Object.keys(method).filter((key) => !['args', 'mutates', 'name', 'selector', 'return_type'].includes(key));
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for messages.${name}`);
assert(isString(method.name) && isNumber(method.selector) && (isNull(method.return_type) || isString(method.return_type)), `Expected name, selector & return_type specified for messages.${method.name}`);
validateArgs(`messages.${method.name}`, method.args);
});
}
export function validateAbi (abi: ContractABI): void {
const unknownKeys = Object.keys(abi).filter((key) => !['deploy', 'messages', 'name'].includes(key));
assert(unknownKeys.length === 0, `Unknown fields ${unknownKeys.join(', ')} found in ABI`);
assert(abi.deploy && abi.messages && isString(abi.name), 'ABI should have deploy, messages & name sections');
validateDeploy(abi);
validateMethods(abi);
}
export default class ContractAbi implements Contract {
abi: ContractABI;
deploy: ContractABIFn;
messages: { [index: string]: ContractABIFn } = {};
constructor (abi: ContractABI) {
validateAbi(abi);
this.abi = abi;
this.deploy = this._createEncoded('deploy', abi.deploy);
abi.messages.forEach((method) => {
const name = stringCamelCase(method.name);
this.messages[name] = this._createEncoded(`messages.${name}`, method);
});
}
private _createClazz (args: ContractABIArgs, isDeploy: boolean): Constructor {
return createClass(
JSON.stringify(
args.reduce((base: { [index: string]: string }, { name, type }) => {
base[name] = type;
return base;
}, isDeploy ? {} : { __selector: 'u32' })
)
);
}
private _createEncoded (name: string, method: Partial<ContractABIMethod> & ContractABIMethodBase): ContractABIFn {
const args = method.args.map(({ name, type }) => ({
name: stringCamelCase(name),
type
}));
const Clazz = this._createClazz(args, isUndefined(method.selector));
const base: { [index: string]: any } = { __selector: method.selector };
const encoder = (...params: Array<CodecArg>): Uint8Array => {
assert(params.length === args.length, `Expected ${args.length} arguments to contract ${name}, found ${params.length}`);
return Compact.addLengthPrefix(
new Clazz(
args.reduce((mapped, { name }, index) => {
mapped[name] = params[index];
return mapped;
}, { ...base })
).toU8a()
);
};
const fn = (encoder as ContractABIFn);
fn.args = args;
fn.isConstant = !(method.mutates || false);
fn.type = method.return_type || null;
return fn;
}
}
@@ -0,0 +1,15 @@
// 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 MagicNumber, { MAGIC_NUMBER } from './MagicNumber';
describe('MagicNumber', () => {
it('succeeds when the magic number matches', () => {
expect(() => new MagicNumber(MAGIC_NUMBER)).not.toThrow();
});
it('fails when the magic number missmatches', () => {
expect(() => new MagicNumber(0x12345)).toThrow(/MagicNumber/);
});
});
@@ -0,0 +1,18 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { assert } from '@plugnet/util';
import U32 from '../primitive/U32';
export const MAGIC_NUMBER = new U32(0x6174656d); // `meta`, reversed for LE encoding
export const MAGIC_ERROR = -61746;
export default class MagicNumber extends U32 {
constructor (value?: any) {
super(value);
assert(this.eq(MAGIC_NUMBER), `MagicNumber: expected ${MAGIC_NUMBER.toHex()}, found ${this.toHex()}`, MAGIC_ERROR);
}
}
+44
View File
@@ -0,0 +1,44 @@
// 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 { isHex, hexToU8a, u8aConcat } from '@plugnet/util';
import { MAGIC_ERROR, MAGIC_NUMBER } from './MagicNumber';
import MetadataVersioned from './MetadataVersioned';
/**
* @name Metadata
* @description
* The versioned runtime metadata as a decoded structure
*/
export default class Metadata extends MetadataVersioned {
constructor (value?: Uint8Array | string) {
super(Metadata.decodeMetadata(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 {
const value = isHex(_value)
? hexToU8a(_value)
: _value;
try {
return new MetadataVersioned(value);
} catch (error) {
if (error.code !== MAGIC_ERROR) {
throw error;
}
}
return new MetadataVersioned(
u8aConcat(
MAGIC_NUMBER.toU8a(), // manually add the magic number
Uint8Array.from([0]), // add the version for the original
value // the actual data as retrieved
)
);
}
}
@@ -0,0 +1,166 @@
// 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 { MetadataInterface } from './types';
import { assert, isUndefined } from '@plugnet/util';
import EnumType from '../codec/EnumType';
import Struct from '../codec/Struct';
import Null from '../primitive/Null';
import MagicNumber from './MagicNumber';
import MetadataV0 from './v0';
import MetadataV1 from './v1';
import MetadataV2 from './v2';
import MetadataV3 from './v3';
import v1ToV0 from './v1/toV0';
import v2ToV1 from './v2/toV1';
import v3ToV2 from './v3/toV2';
class MetadataEnum extends EnumType<Null | MetadataV1 | MetadataV2> {
constructor (value?: any) {
super({
MetadataV0, // once rolled-out, can replace this with Null
MetadataV1, // once rolled-out, can replace this with Null
MetadataV2, // once rolled-out, can replace this with Null
MetadataV3
}, value);
}
/**
* @description Returns the wrapped values as a V0 object
*/
get asV0 (): MetadataV0 {
return this.value as MetadataV0;
}
/**
* @description Returns the wrapped values as a V1 object
*/
get asV1 (): MetadataV1 {
return this.value as MetadataV1;
}
/**
* @description Returns the wrapped values as a V2 object
*/
get asV2 (): MetadataV2 {
return this.value as MetadataV2;
}
/**
* @description Returns the wrapped values as a V3 object
*/
get asV3 (): MetadataV3 {
return this.value as MetadataV3;
}
/**
* @description The version this metadata represents
*/
get version (): number {
return this.index;
}
}
/**
* @name MetadataVersioned
* @description
* The versioned runtime metadata as a decoded structure
*/
export default class MetadataVersioned extends Struct implements MetadataInterface {
private _convertedV0?: MetadataV0;
private _convertedV1?: MetadataV1;
private _convertedV2?: MetadataV2;
constructor (value?: any) {
super({
magicNumber: MagicNumber,
metadata: MetadataEnum
}, value);
}
/**
* @description
*/
get magicNumber (): MagicNumber {
return this.get('magicNumber') as MagicNumber;
}
/**
* @description the metadata wrapped
*/
private get metadata (): MetadataEnum {
return this.get('metadata') as MetadataEnum;
}
/**
* @description the metadata version this structure represents
*/
get version (): number {
return this.metadata.index;
}
/**
* @description Returns the wrapped metadata as a V0 object
*/
get asV0 (): MetadataV0 {
if (this.metadata.version === 0) {
return this.metadata.asV0;
}
if (isUndefined(this._convertedV0)) {
this._convertedV0 = v1ToV0(this.asV1);
}
return this._convertedV0;
}
/**
* @description Returns the wrapped values as a V1 object
*/
get asV1 (): MetadataV1 {
if (this.metadata.version === 1) {
return this.metadata.asV1;
}
assert([2, 3].includes(this.metadata.version), `Cannot convert metadata from v${this.metadata.version} to v1`);
if (isUndefined(this._convertedV1)) {
this._convertedV1 = v2ToV1(this.metadata.asV2);
}
return this._convertedV1;
}
/**
* @description Returns the wrapped values as a V2 object
*/
get asV2 (): MetadataV2 {
if (this.metadata.version === 2) {
return this.metadata.asV2;
}
assert(this.metadata.version === 3, `Cannot convert metadata from v${this.metadata.version} to v1`);
if (isUndefined(this._convertedV2)) {
this._convertedV2 = v3ToV2(this.metadata.asV3);
}
return this._convertedV2;
}
/**
* @description Returns the wrapped values as a V3 object
*/
get asV3 (): MetadataV3 {
assert(this.metadata.version === 3, `Cannot convert metadata from v${this.metadata.version} to v3`);
return this.metadata.asV3;
}
getUniqTypes (throwError: boolean): Array<string> {
return (this.metadata.value as any as MetadataInterface).getUniqTypes(throwError);
}
}
+12
View File
@@ -0,0 +1,12 @@
# metadata update
1. New branch :)
2. `curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933`
3. Copy the result (hex-only) into `tstaic.ts`, as of now linked to `v1/static.ts`
4. Rust Metadata tests, `yarn run test packages/types/src/Metadata`
5. Add new class types as required (find missing in Rust code, add)
6. Add all classes to `index.classes.ts`
7. Repeat 4-6 until passing
8. Update parsed JSON to actual output version, `v1/latest.*.json`
9. All tests
10. Make PR
+7
View File
@@ -0,0 +1,7 @@
// 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 { default as Metadata } from './Metadata';
export default Metadata;
+7
View File
@@ -0,0 +1,7 @@
// 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 metadata from './v3/static';
export default metadata;
+7
View File
@@ -0,0 +1,7 @@
// 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.
export interface MetadataInterface {
getUniqTypes (throwError: boolean): Array<string>;
}
@@ -0,0 +1,18 @@
// 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.
// Quick and dirty flatten (.flat() not available)
export default function flattenUniq (list: Array<any>): Array<any> {
const flat = list.reduce((result, entry) => {
return result.concat(
Array.isArray(entry)
? flattenUniq(entry)
: entry
);
}, []);
return [...new Set(flat)]
.filter((value: any) => value)
.sort();
}
@@ -0,0 +1,6 @@
// 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.
export { default as flattenUniq } from './flattenUniq';
export { default as validateTypes } from './validateTypes';
@@ -0,0 +1,50 @@
// 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 { isUndefined } from '@plugnet/util';
import { getTypeDef, TypeDef, TypeDefInfo } from '../../codec/createType';
import flattenUniq from './flattenUniq';
import { getTypeRegistry } from '../../codec';
export default function validateTypes (types: Array<string>, throwError: boolean): void {
const extractTypes = (types: Array<string>): Array<any> => {
return types.map((type) => {
const decoded = getTypeDef(type);
switch (decoded.info) {
case TypeDefInfo.Plain:
return decoded.type;
case TypeDefInfo.Compact:
case TypeDefInfo.Option:
case TypeDefInfo.Vector:
return extractTypes([(decoded.sub as TypeDef).type]);
case TypeDefInfo.Tuple:
return extractTypes(
(decoded.sub as Array<TypeDef>).map((sub) => sub.type)
);
default:
throw new Error('Unreachable');
}
});
};
const typeRegistry = getTypeRegistry();
const missing = flattenUniq(extractTypes(types)).filter((type) =>
isUndefined(typeRegistry.get(type))
);
if (missing.length !== 0) {
const message = `Unknown types found, no types for ${missing}`;
if (throwError) {
throw new Error(message);
} else {
console.error(message);
}
}
}
+62
View File
@@ -0,0 +1,62 @@
// 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 Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import U16 from '../../primitive/U16';
export class OuterDispatchCall extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
index: U16
}, value);
}
/**
* @description The [[U16]] index for the call
*/
get index (): U16 {
return this.get('index') as U16;
}
/**
* @description The name for the call
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The call prefix (or section)
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
}
export class OuterDispatchMetadata extends Struct {
constructor (value?: any) {
super({
name: Text,
calls: Vector.with(OuterDispatchCall)
}, value);
}
/**
* @description The [[OuterDispathCall]] wrapped
*/
get calls (): Vector<OuterDispatchCall> {
return this.get('calls') as Vector<OuterDispatchCall>;
}
/**
* @description The name for the dispatch
*/
get name (): Text {
return this.get('name') as Text;
}
}
+86
View File
@@ -0,0 +1,86 @@
// 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 Struct from '../../codec/Struct';
import Tuple from '../../codec/Tuple';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
export class EventMetadata extends Struct {
constructor (value?: any) {
super({
name: Text,
arguments: Vector.with(Type),
documentation: Vector.with(Text)
}, value);
}
/**
* @description The arguments of [[Type]]
*/
get arguments (): Vector<Type> {
return this.get('arguments') as Vector<Type>;
}
/**
* @description The [[Text]] documentation
*/
get documentation (): Vector<Text> {
return this.get('documentation') as Vector<Text>;
}
/**
* @description The name for the event
*/
get name (): Text {
return this.get('name') as Text;
}
}
export class OuterEventMetadataEvent extends Tuple {
constructor (value?: any) {
super({
Text,
'Vec<EventMetadata>': Vector.with(EventMetadata)
}, value);
}
/**
* @description The [[EventMetadata]]
*/
get events (): Vector<EventMetadata> {
return this[1] as Vector<EventMetadata>;
}
/**
* @description The name of the section
*/
get name (): Text {
return this[0] as Text;
}
}
export class OuterEventMetadata extends Struct {
constructor (value?: any) {
super({
name: Text,
events: Vector.with(OuterEventMetadataEvent)
}, value);
}
/**
* @description The [[OuterEventMetadataEvent]]
*/
get events (): Vector<OuterEventMetadataEvent> {
return this.get('events') as Vector<OuterEventMetadataEvent>;
}
/**
* @description The name of the event
*/
get name (): Text {
return this.get('name') as Text;
}
}
@@ -0,0 +1,42 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata';
import createType from '../../codec/createType';
import Method from '../../primitive/Method';
import Metadata from './Metadata';
import latestParsed from './latest.substrate.json';
import rpcData from './static';
describe('Metadata', () => {
it('decodes latest properly', () => {
const metadata = new Metadata(rpcData);
const str = JSON.stringify(metadata.toJSON());
expect(metadata.events.length).not.toBe(0);
expect(str).toEqual(JSON.stringify(latestParsed));
});
describe('storage with default values', () => {
const metadata = new Metadata(rpcData);
Method.injectMethods(extrinsicsFromMeta(metadata));
metadata.modules.forEach((mod) => {
if (mod.storage.isNone) {
return;
}
mod.storage.unwrap().functions.forEach((fn) => {
it(`creates default types for ${mod.prefix}.${fn.name}, type ${fn.type}`, () => {
expect(
() => createType(fn.type.toString(), fn.default)
).not.toThrow();
});
});
});
});
});
+116
View File
@@ -0,0 +1,116 @@
// 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 { MetadataInterface } from '../types';
import { hexToU8a, isHex, isU8a } from '@plugnet/util';
import Compact from '../../codec/Compact';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import { flattenUniq, validateTypes } from '../util';
import { OuterDispatchMetadata, OuterDispatchCall } from './Calls';
import { OuterEventMetadata, OuterEventMetadataEvent } from './Events';
import { RuntimeModuleMetadata } from './Modules';
// Decodes the runtime metadata as passed through from the `state_getMetadata` call. This
// file is probably best understood from the bottom-up, i.e. start reading right at the
// end and work up. (Just so we don't use before definition)
/**
* @name MetadataV0
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV0 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
outerEvent: OuterEventMetadata,
modules: Vector.with(RuntimeModuleMetadata),
outerDispatch: OuterDispatchMetadata
}, MetadataV0.decodeMetadata(value));
}
static decodeMetadata (value: string | Uint8Array | object): object | Uint8Array {
if (isHex(value)) {
// We receive this as an hex in the JSON output from the Node.
// Convert to u8a and use the U8a version to do the actual parsing.
return MetadataV0.decodeMetadata(hexToU8a(value));
} else if (isU8a(value)) {
// HACK 13 Oct 2018 - For current running BBQ nodes, Metadata is not properly
// encoded, it does not have a length prefix. For latest substrate master, it
// is properly encoded. Here we pull the prefix, check it agianst the length -
// if matches, then we have the length, otherwise we assume it is an older node
// and use the whole buffer
const [offset, length] = Compact.decodeU8a(value);
return value.length === (offset + length.toNumber())
? value.subarray(offset)
: value;
}
// Decode as normal struct
return value;
}
/**
* @description Wrapped [[OuterDispatchCall]]
*/
get calls (): Vector<OuterDispatchCall> {
return (this.get('outerDispatch') as OuterDispatchMetadata).calls;
}
/**
* @description Wrapped [[OuterEventMetadataEvent]]
*/
get events (): Vector<OuterEventMetadataEvent> {
return (this.get('outerEvent') as OuterEventMetadata).events;
}
/**
* @description Wrapped [[RuntimeModuleMetadata]]
*/
get modules (): Vector<RuntimeModuleMetadata> {
return this.get('modules') as Vector<RuntimeModuleMetadata>;
}
private get argNames () {
return this.modules.map((module) =>
module.module.call.functions.map((fn) =>
fn.arguments.map((argument) => argument.type.toString())
)
);
}
private get eventNames () {
return this.events.map((module) =>
module.events.map((event) =>
event.arguments.map((argument) => argument.toString())
)
);
}
private get storageNames () {
return this.modules.map((module) =>
module.storage.isSome
? module.storage.unwrap().functions.map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
: []
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.argNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+327
View File
@@ -0,0 +1,327 @@
// 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 { AnyNumber } from '../../types';
import Enum from '../../codec/Enum';
import EnumType from '../../codec/EnumType';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Bytes from '../../primitive/Bytes';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
import U16 from '../../primitive/U16';
export class FunctionArgumentMetadata extends Struct {
constructor (value?: any) {
super({
name: Text,
type: Type
}, value);
}
/**
* @description The argument name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The [[Type]]
*/
get type (): Type {
return this.get('type') as Type;
}
}
export class FunctionMetadata extends Struct {
constructor (value?: any) {
super({
id: U16,
name: Text,
arguments: Vector.with(FunctionArgumentMetadata),
documentation: Vector.with(Text)
}, value);
}
/**
* @description The [[FunctionArgumentMetadata]] for arguments
*/
get arguments (): Vector<FunctionArgumentMetadata> {
return this.get('arguments') as Vector<FunctionArgumentMetadata>;
}
/**
* @description The [[Text]] documentation
*/
get documentation (): Vector<Text> {
return this.get('documentation') as Vector<Text>;
}
/**
* @description The `[sectionIndex, methodIndex]` call id
*/
get id (): U16 {
return this.get('id') as U16;
}
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
}
export class CallMetadata extends Struct {
constructor (value?: any) {
super({
name: Text,
functions: Vector.with(FunctionMetadata)
}, value);
}
/**
* @description The functions available as [[FunctionMetadata]]
*/
get functions (): Vector<FunctionMetadata> {
return this.get('functions') as Vector<FunctionMetadata>;
}
/**
* @description The section name
*/
get name (): Text {
return this.get('name') as Text;
}
}
export class ModuleMetadata extends Struct {
constructor (value?: any) {
super({
name: Text,
call: CallMetadata
}, value);
}
/**
* @description The calls as [[CallMetadata]]
*/
get call (): CallMetadata {
return this.get('call') as CallMetadata;
}
/**
* @description The name
*/
get name (): Text {
return this.get('name') as Text;
}
}
export class StorageFunctionModifier extends Enum {
constructor (value?: any) {
super(['Optional', 'Default', 'Required'], value);
}
/**
* @description `true` if the storage entry is optional
*/
get isOptional (): boolean {
return this.toNumber() === 0;
}
}
export class MapType extends Struct {
private _isLinked = false;
constructor (value?: any) {
super({
key: Type,
value: Type
}, value);
if (value && value.isLinked) {
this._isLinked = true;
}
}
/**
* @description The mapped key as [[Type]]
*/
get key (): Type {
return this.get('key') as Type;
}
/**
* @description The mapped value as [[Type]]
*/
get value (): Type {
return this.get('value') as Type;
}
/**
* @description Is this an enumerable linked map
*/
get isLinked (): boolean {
return this._isLinked;
}
}
class PlainType extends Type {
}
export class StorageFunctionType extends EnumType<PlainType | MapType> {
constructor (value?: any, index?: number) {
super({
PlainType,
MapType
}, value, index);
}
/**
* @description `true` if the storage entry is a map
*/
get isMap (): boolean {
return this.toNumber() === 1;
}
/**
* @description The value as a mapped value
*/
get asMap (): MapType {
return this.value as MapType;
}
/**
* @description The value as a [[Type]] value
*/
get asType (): PlainType {
return this.value as PlainType;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
if (this.isMap) {
if (this.asMap.isLinked) {
return `(${this.asMap.value.toString()}, Linkage<${this.asMap.key.toString()}>)`;
}
return this.asMap.value.toString();
}
return this.asType.toString();
}
}
type StorageFunctionMetadataValue = {
name: string | Text,
modifier: StorageFunctionModifier | AnyNumber,
type: StorageFunctionType,
default: Bytes,
documentation: Vector<Text> | Array<string>
};
export class StorageFunctionMetadata extends Struct {
constructor (value?: StorageFunctionMetadataValue | Uint8Array) {
super({
name: Text,
modifier: StorageFunctionModifier,
type: StorageFunctionType,
default: Bytes,
documentation: Vector.with(Text)
}, value);
}
/**
* @description The default value of the storage function
*/
get default (): Bytes {
return this.get('default') as Bytes;
}
/**
* @description The [[Text]] documentation
*/
get documentation (): Vector<Text> {
return this.get('documentation') as Vector<Text>;
}
/**
* @description The key name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The modifier
*/
get modifier (): StorageFunctionModifier {
return this.get('modifier') as StorageFunctionModifier;
}
/**
* @description The [[StorageFunctionType]]
*/
get type (): StorageFunctionType {
return this.get('type') as StorageFunctionType;
}
}
export class StorageMetadata extends Struct {
constructor (value?: any) {
super({
prefix: Text,
functions: Vector.with(StorageFunctionMetadata)
}, value);
}
/**
* @description The [[StorageFunctionMetadata]] for the section
*/
get functions (): Vector<StorageFunctionMetadata> {
return this.get('functions') as Vector<StorageFunctionMetadata>;
}
/**
* @description The section prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
}
export class RuntimeModuleMetadata extends Struct {
constructor (value?: any) {
super({
prefix: Text,
module: ModuleMetadata,
storage: Option.with(StorageMetadata)
}, value);
}
/**
* @description The [[ModuleMetadata]]
*/
get module (): ModuleMetadata {
return this.get('module') as ModuleMetadata;
}
/**
* @description The prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description The optional [[StorageMetadata]]
*/
get storage (): Option<StorageMetadata> {
return this.get('storage') as Option<StorageMetadata>;
}
}
+7
View File
@@ -0,0 +1,7 @@
// 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 { default as MetadataV0 } from './Metadata';
export default MetadataV0;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+77
View File
@@ -0,0 +1,77 @@
// 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 Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
export class MetadataCallArg extends Struct {
constructor (value?: any) {
super({
name: Text,
type: Type
}, value);
}
/**
* @description The argument name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The [[Type]]
*/
get type (): Type {
return this.get('type') as Type;
}
}
/**
* @name MetadataCall
* @description
* The definition of a call
*/
export class MetadataCall extends Struct {
constructor (value?: any) {
super({
// NOTE Just commented out, it is not available in current, but an open question
// around the usefulness of supplying this remains (could be re-added)
// id: u16,
name: Text,
args: Vector.with(MetadataCallArg),
docs: Vector.with(Text)
}, value);
}
/**
* @description The [[MetadataCallArg]] for arguments
*/
get args (): Vector<MetadataCallArg> {
return this.get('args') as Vector<MetadataCallArg>;
}
/**
* @description The [[Text]] documentation
*/
get docs (): Vector<Text> {
return this.get('docs') as Vector<Text>;
}
// /**
// * @description The call function id
// */
// get id (): u16 {
// return this.get('id') as u16;
// }
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
}
+44
View File
@@ -0,0 +1,44 @@
// 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 Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
/**
* @name MetadataEvent
* @description
* The definition of an event
*/
export class MetadataEvent extends Struct {
constructor (value?: any) {
super({
name: Text,
args: Vector.with(Type),
docs: Vector.with(Text)
}, value);
}
/**
* @description The [[Type]] for args
*/
get args (): Vector<Type> {
return this.get('args') as Vector<Type>;
}
/**
* @description The [[Text]] documentation
*/
get docs (): Vector<Text> {
return this.get('docs') as Vector<Text>;
}
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
}
@@ -0,0 +1,46 @@
// 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 extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata';
import createType from '../../codec/createType';
import Method from '../../primitive/Method';
import Metadata from '../Metadata';
import latestParsed from './latest.substrate.v1.json';
import rpcData from './static';
describe('MetadataV1', () => {
const metadata = new Metadata(rpcData);
it('decodes latest properly', () => {
const str = JSON.stringify(metadata.toJSON());
expect(metadata.version).toBe(1);
expect(metadata.asV1.modules.length).not.toBe(0);
expect(str).toEqual(JSON.stringify(latestParsed));
});
it('converts v1 to V0', () => {
const v0 = metadata.asV0;
expect(metadata.getUniqTypes(true)).toEqual(v0.getUniqTypes(true));
});
describe('storage with default values', () => {
Method.injectMethods(extrinsicsFromMeta(metadata.asV0));
metadata.asV1.modules
.filter(({ storage }) => storage.isSome)
.map((mod) =>
mod.storage.unwrap().forEach(({ fallback, name, type }) => {
it(`creates default types for ${mod.prefix}.${name}, type ${type}`, () => {
expect(
() => createType(type.toString(), fallback)
).not.toThrow();
});
})
);
});
});
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import { flattenUniq, validateTypes } from '../util';
import { MetadataCall } from './Calls';
import { MetadataEvent } from './Events';
import { MetadataStorage } from './Storage';
/**
* @name MetadataModule
* @description
* The definition of a module in the system
*/
export class MetadataModule extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
storage: Option.with(Vector.with(MetadataStorage)),
calls: Option.with(Vector.with(MetadataCall)),
events: Option.with(Vector.with(MetadataEvent))
}, value);
}
/**
* @description the module calls
*/
get calls (): Option<Vector<MetadataCall>> {
return this.get('calls') as Option<Vector<MetadataCall>>;
}
/**
* @description the module events
*/
get events (): Option<Vector<MetadataEvent>> {
return this.get('events') as Option<Vector<MetadataEvent>>;
}
/**
* @description the module name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description the module prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description the associated module storage
*/
get storage (): Option<Vector<MetadataStorage>> {
return this.get('storage') as Option<Vector<MetadataStorage>>;
}
}
/**
* @name MetadataV1
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV1 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
modules: Vector.with(MetadataModule)
}, value);
}
/**
* @description The associated modules for this structure
*/
get modules (): Vector<MetadataModule> {
return this.get('modules') as Vector<MetadataModule>;
}
private get callNames () {
return this.modules.map((mod) =>
mod.calls.isNone
? []
: mod.calls.unwrap().map((fn) =>
fn.args.map((arg) => arg.type.toString())
)
);
}
private get eventNames () {
return this.modules.map((mod) =>
mod.events.isNone
? []
: mod.events.unwrap().map((event) =>
event.args.map((arg) => arg.toString())
)
);
}
private get storageNames () {
return this.modules.map((mod) =>
mod.storage.isNone
? []
: mod.storage.unwrap().map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+165
View File
@@ -0,0 +1,165 @@
// 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 EnumType from '../../codec/EnumType';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Bytes from '../../primitive/Bytes';
import Null from '../../primitive/Null';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
export class Default extends Null {}
export class Optional extends Null {}
export class MetadataStorageModifier extends EnumType<Optional | Default> {
constructor (value?: any, index?: number) {
super({
Optional,
Default
}, value, index);
}
/**
* @description `true` if the storage entry is optional
*/
get isOptional (): boolean {
return this.toNumber() === 0;
}
toJSON (): any {
return this.toString();
}
}
export class MapType extends Struct {
private _isLinked = false;
constructor (value?: any) {
super({
key: Type,
value: Type
}, value);
if (value && value.isLinked) {
this._isLinked = true;
}
}
/**
* @description The mapped key as [[Type]]
*/
get key (): Type {
return this.get('key') as Type;
}
/**
* @description The mapped value as [[Type]]
*/
get value (): Type {
return this.get('value') as Type;
}
/**
* @description Is this an enumerable linked map
*/
get isLinked (): boolean {
return this._isLinked;
}
}
export class PlainType extends Type {
}
export class MetadataStorageType extends EnumType<PlainType | MapType> {
constructor (value?: any, index?: number) {
super({
PlainType,
MapType
}, value, index);
}
/**
* @description `true` if the storage entry is a map
*/
get isMap (): boolean {
return this.toNumber() === 1;
}
/**
* @description The value as a mapped value
*/
get asMap (): MapType {
return this.value as MapType;
}
/**
* @description The value as a [[Type]] value
*/
get asType (): PlainType {
return this.value as PlainType;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.isMap
? this.asMap.value.toString()
: this.asType.toString();
}
}
/**
* @name MetadataModule
* @description
* The definition of a storage function
*/
export class MetadataStorage extends Struct {
constructor (value?: any) {
super({
name: Text,
modifier: MetadataStorageModifier,
type: MetadataStorageType,
fallback: Bytes,
docs: Vector.with(Text)
}, value);
}
/**
* @description The [[Text]] documentation
*/
get docs (): Vector<Text> {
return this.get('docs') as Vector<Text>;
}
/**
* @description The [[Bytes]] fallback default
*/
get fallback (): Bytes {
return this.get('fallback') as Bytes;
}
/**
* @description The [[MetadataArgument]] for arguments
*/
get modifier (): MetadataStorageModifier {
return this.get('modifier') as MetadataStorageModifier;
}
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The [[MetadataStorageType]]
*/
get type (): MetadataStorageType {
return this.get('type') as MetadataStorageType;
}
}
+11
View File
@@ -0,0 +1,11 @@
// 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 { default as MetadataV1, MetadataModule as MetadataModuleV1 } from './Metadata';
export default MetadataV1;
export {
MetadataModuleV1
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+102
View File
@@ -0,0 +1,102 @@
// 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 { OuterDispatchCall, OuterDispatchMetadata } from '../v0/Calls';
import { EventMetadata, OuterEventMetadata, OuterEventMetadataEvent } from '../v0/Events';
import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata, StorageFunctionType } from '../v0/Modules';
import MetadataV0 from '../v0/Metadata';
import MetadataV1, { MetadataModuleV1 } from '.';
function storageV0 (mod: MetadataModuleV1): StorageMetadata | null {
if (mod.storage.isNone) {
return null;
}
return new StorageMetadata({
prefix: mod.prefix,
functions: mod.storage.unwrap().map(({ docs, fallback, modifier, name, type }) =>
new StorageFunctionMetadata({
name,
modifier: modifier.toNumber(),
type: new StorageFunctionType(type),
default: fallback,
documentation: docs
})
)
});
}
function moduleV0 (mod: MetadataModuleV1): ModuleMetadata {
return new ModuleMetadata({
name: 'Module',
call: new CallMetadata({
name: 'Call',
functions: mod.calls.isNone
? []
: mod.calls.unwrap().map(({ args, docs, name }, id) =>
new FunctionMetadata({
id,
name,
arguments: args,
documentation: docs
})
)
})
});
}
function modulesV0 (v1: MetadataV1): Array<RuntimeModuleMetadata> {
return v1.modules.map((mod) =>
new RuntimeModuleMetadata({
prefix: mod.name, // passed from name, compact with casing
module: moduleV0(mod),
storage: storageV0(mod)
})
);
}
function outerDispatchV0 (v1: MetadataV1): OuterDispatchMetadata {
return new OuterDispatchMetadata({
name: 'Call',
calls: v1.modules
.filter(({ calls }) => calls.isSome)
.map((mod, index) =>
new OuterDispatchCall({
// name and prefix are swapped in the actual V0 data, i.e. name is UpperCase
name: mod.prefix,
prefix: mod.name,
index
})
)
});
}
function outerEventV0 (v1: MetadataV1): OuterEventMetadata {
return new OuterEventMetadata({
name: 'Event',
events: v1.modules
.filter(({ events }) => events.isSome)
.map((mod) =>
new OuterEventMetadataEvent([
mod.name,
mod.events.unwrap().map(({ args, docs, name }) =>
new EventMetadata({
name,
arguments: args,
documentation: docs
})
)
])
)
});
}
export default function toV0 (v1: MetadataV1): MetadataV0 {
return new MetadataV0({
outerEvent: outerEventV0(v1),
modules: modulesV0(v1),
outerDispatch: outerDispatchV0(v1)
});
}
@@ -0,0 +1,52 @@
// 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 extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata';
import createType from '../../codec/createType';
import Method from '../../primitive/Method';
import Metadata from '../Metadata';
import latestParsed from './latest.substrate.v2.json';
import rpcData from './static';
describe('MetadataV2', () => {
const metadata = new Metadata(rpcData);
it('decodes latest properly', () => {
const str = JSON.stringify(metadata.toJSON());
expect(metadata.version).toBe(2);
expect(metadata.asV2.modules.length).not.toBe(0);
expect(str).toEqual(JSON.stringify(latestParsed));
});
it('converts v2 to V1', () => {
const v1 = metadata.asV1;
expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true));
});
it('converts v2 to V0', () => {
const v1 = metadata.asV0;
expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true));
});
describe('storage with default values', () => {
Method.injectMethods(extrinsicsFromMeta(metadata.asV0));
metadata.asV2.modules
.filter(({ storage }) => storage.isSome)
.map((mod) =>
mod.storage.unwrap().forEach(({ fallback, name, type }) => {
it(`creates default types for ${mod.prefix}.${name}, type ${type}`, () => {
expect(
() => createType(type.toString(), fallback)
).not.toThrow();
});
})
);
});
});
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import { flattenUniq, validateTypes } from '../util';
import { MetadataCall } from '../v1/Calls';
import { MetadataEvent } from '../v1/Events';
import { MetadataStorage } from './Storage';
/**
* @name MetadataModule
* @description
* The definition of a module in the system
*/
export class MetadataModule extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
storage: Option.with(Vector.with(MetadataStorage)),
calls: Option.with(Vector.with(MetadataCall)),
events: Option.with(Vector.with(MetadataEvent))
}, value);
}
/**
* @description the module calls
*/
get calls (): Option<Vector<MetadataCall>> {
return this.get('calls') as Option<Vector<MetadataCall>>;
}
/**
* @description the module events
*/
get events (): Option<Vector<MetadataEvent>> {
return this.get('events') as Option<Vector<MetadataEvent>>;
}
/**
* @description the module name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description the module prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description the associated module storage
*/
get storage (): Option<Vector<MetadataStorage>> {
return this.get('storage') as Option<Vector<MetadataStorage>>;
}
}
/**
* @name MetadataV2
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV2 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
modules: Vector.with(MetadataModule)
}, value);
}
/**
* @description The associated modules for this structure
*/
get modules (): Vector<MetadataModule> {
return this.get('modules') as Vector<MetadataModule>;
}
private get callNames () {
return this.modules.map((mod) =>
mod.calls.isNone
? []
: mod.calls.unwrap().map((fn) =>
fn.args.map((arg) => arg.type.toString())
)
);
}
private get eventNames () {
return this.modules.map((mod) =>
mod.events.isNone
? []
: mod.events.unwrap().map((event) =>
event.args.map((arg) => arg.toString())
)
);
}
private get storageNames () {
return this.modules.map((mod) =>
mod.storage.isNone
? []
: mod.storage.unwrap().map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+142
View File
@@ -0,0 +1,142 @@
// 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 EnumType from '../../codec/EnumType';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Bool from '../../primitive/Bool';
import Bytes from '../../primitive/Bytes';
import Null from '../../primitive/Null';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
import { MetadataStorageModifier } from '../v1/Storage';
export class Default extends Null {}
export class Optional extends Null {}
export class MapType extends Struct {
constructor (value?: any) {
super({
key: Type,
value: Type,
isLinked: Bool
}, value);
}
/**
* @description The mapped key as [[Type]]
*/
get key (): Type {
return this.get('key') as Type;
}
/**
* @description The mapped value as [[Type]]
*/
get value (): Type {
return this.get('value') as Type;
}
/**
* @description Is this an enumerable linked map
*/
get isLinked (): boolean {
return (this.get('isLinked') as Bool).valueOf();
}
}
export class PlainType extends Type {
}
export class MetadataStorageType extends EnumType<PlainType | MapType> {
constructor (value?: any, index?: number) {
super({
PlainType,
MapType
}, value, index);
}
/**
* @description `true` if the storage entry is a map
*/
get isMap (): boolean {
return this.toNumber() === 1;
}
/**
* @description The value as a mapped value
*/
get asMap (): MapType {
return this.value as MapType;
}
/**
* @description The value as a [[Type]] value
*/
get asType (): PlainType {
return this.value as PlainType;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.isMap
? this.asMap.value.toString()
: this.asType.toString();
}
}
/**
* @name MetadataModule
* @description
* The definition of a storage function
*/
export class MetadataStorage extends Struct {
constructor (value?: any) {
super({
name: Text,
modifier: MetadataStorageModifier,
type: MetadataStorageType,
fallback: Bytes,
docs: Vector.with(Text)
}, value);
}
/**
* @description The [[Text]] documentation
*/
get docs (): Vector<Text> {
return this.get('docs') as Vector<Text>;
}
/**
* @description The [[Bytes]] fallback default
*/
get fallback (): Bytes {
return this.get('fallback') as Bytes;
}
/**
* @description The [[MetadataArgument]] for arguments
*/
get modifier (): MetadataStorageModifier {
return this.get('modifier') as MetadataStorageModifier;
}
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The [[MetadataStorageType]]
*/
get type (): MetadataStorageType {
return this.get('type') as MetadataStorageType;
}
}
+11
View File
@@ -0,0 +1,11 @@
// 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 { default as MetadataV2, MetadataModule as MetadataModuleV2 } from './Metadata';
export default MetadataV2;
export {
MetadataModuleV2
};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
// 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 MetadataV1 from '../v1/Metadata';
import MetadataV2 from '.';
export default function toV1 (v2: MetadataV2): MetadataV1 {
return new MetadataV1(v2);
}
@@ -0,0 +1,22 @@
// 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 Metadata from '../Metadata';
import latestParsed from './latest.substrate.v3.json';
import rpcData from './static';
describe('MetadataV3', () => {
const metadata = new Metadata(rpcData);
it('decodes latest properly', () => {
const str = JSON.stringify(metadata.toJSON());
console.error(str);
console.error(metadata.getUniqTypes(true));
expect(metadata.version).toBe(3);
expect(metadata.asV3.modules.length).not.toBe(0);
expect(str).toEqual(JSON.stringify(latestParsed));
});
});
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import { flattenUniq, validateTypes } from '../util';
import { MetadataCall } from '../v1/Calls';
import { MetadataEvent } from '../v1/Events';
import { MetadataStorage } from './Storage';
/**
* @name MetadataModule
* @description
* The definition of a module in the system
*/
export class MetadataModule extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
storage: Option.with(Vector.with(MetadataStorage)),
calls: Option.with(Vector.with(MetadataCall)),
events: Option.with(Vector.with(MetadataEvent))
}, value);
}
/**
* @description the module calls
*/
get calls (): Option<Vector<MetadataCall>> {
return this.get('calls') as Option<Vector<MetadataCall>>;
}
/**
* @description the module events
*/
get events (): Option<Vector<MetadataEvent>> {
return this.get('events') as Option<Vector<MetadataEvent>>;
}
/**
* @description the module name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description the module prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description the associated module storage
*/
get storage (): Option<Vector<MetadataStorage>> {
return this.get('storage') as Option<Vector<MetadataStorage>>;
}
}
/**
* @name MetadataV3
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV3 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
modules: Vector.with(MetadataModule)
}, value);
}
/**
* @description The associated modules for this structure
*/
get modules (): Vector<MetadataModule> {
return this.get('modules') as Vector<MetadataModule>;
}
private get callNames () {
return this.modules.map((mod) =>
mod.calls.isNone
? []
: mod.calls.unwrap().map((fn) =>
fn.args.map((arg) => arg.type.toString())
)
);
}
private get eventNames () {
return this.modules.map((mod) =>
mod.events.isNone
? []
: mod.events.unwrap().map((event) =>
event.args.map((arg) => arg.toString())
)
);
}
private get storageNames () {
return this.modules.map((mod) =>
mod.storage.isNone
? []
: mod.storage.unwrap().map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+160
View File
@@ -0,0 +1,160 @@
// 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 EnumType from '../../codec/EnumType';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Bytes from '../../primitive/Bytes';
import Text from '../../primitive/Text';
import { MetadataStorageModifier } from '../v1/Storage';
import { MapType, PlainType } from '../v2/Storage';
export class DoubleMapType extends Struct {
constructor (value?: any) {
super({
key1: Text,
key2: Text,
value: Text,
keyHasher: Text
}, value);
}
/**
* @description The mapped key as [[Text]]
*/
get key1 (): Text {
return this.get('key1') as Text;
}
/**
* @description The mapped key as [[Text]]
*/
get key2 (): Text {
return this.get('key2') as Text;
}
/**
* @description The mapped key as [[Text]]
*/
get keyHasher (): Text {
return this.get('keyHasher') as Text;
}
/**
* @description The mapped key as [[Text]]
*/
get value (): Text {
return this.get('value') as Text;
}
}
export class MetadataStorageType extends EnumType<PlainType | MapType | DoubleMapType> {
constructor (value?: any, index?: number) {
super({
PlainType,
MapType,
DoubleMapType
}, value, index);
}
/**
* @description The value as a mapped value
*/
get asDoubleMap (): DoubleMapType {
return this.value as DoubleMapType;
}
/**
* @description The value as a mapped value
*/
get asMap (): MapType {
return this.value as MapType;
}
/**
* @description The value as a [[Type]] value
*/
get asType (): PlainType {
return this.value as PlainType;
}
/**
* @description `true` if the storage entry is a doublemap
*/
get isDoubleMap (): boolean {
return this.toNumber() === 2;
}
/**
* @description `true` if the storage entry is a map
*/
get isMap (): boolean {
return this.toNumber() === 1;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
if (this.isDoubleMap) {
return this.asDoubleMap.toString();
}
return this.isMap
? this.asMap.value.toString()
: this.asType.toString();
}
}
/**
* @name MetadataModule
* @description
* The definition of a storage function
*/
export class MetadataStorage extends Struct {
constructor (value?: any) {
super({
name: Text,
modifier: MetadataStorageModifier,
type: MetadataStorageType,
fallback: Bytes,
docs: Vector.with(Text)
}, value);
}
/**
* @description The [[Text]] documentation
*/
get docs (): Vector<Text> {
return this.get('docs') as Vector<Text>;
}
/**
* @description The [[Bytes]] fallback default
*/
get fallback (): Bytes {
return this.get('fallback') as Bytes;
}
/**
* @description The [[MetadataArgument]] for arguments
*/
get modifier (): MetadataStorageModifier {
return this.get('modifier') as MetadataStorageModifier;
}
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The [[MetadataStorageType]]
*/
get type (): MetadataStorageType {
return this.get('type') as MetadataStorageType;
}
}
+11
View File
@@ -0,0 +1,11 @@
// 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 { default as MetadataV3, MetadataModule as MetadataModuleV3 } from './Metadata';
export default MetadataV3;
export {
MetadataModuleV3
};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
// 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 MetadataV2 from '../v2/Metadata';
import MetadataV3 from '.';
export default function toV2 (v2: MetadataV3): MetadataV2 {
return new MetadataV2(v2);
}
+7
View File
@@ -0,0 +1,7 @@
// 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 * as Classes from './index.types';
export type CodecTypes = keyof typeof Classes;
+131
View File
@@ -0,0 +1,131 @@
// 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 { AnyNumber, Codec } from '../types';
import BN from 'bn.js';
import { bnToBn, hexToBn, isHex, isString, isU8a, u8aToBn } from '@plugnet/util';
export type UIntBitLength = 8 | 16 | 32 | 64 | 128 | 256;
export const DEFAULT_UINT_BITS = 64;
/**
* @name AbstractInt
* @ignore
* @noInheritDoc
*/
// 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 {
protected _bitLength: UIntBitLength;
private _isHexJson: boolean;
private _isNegative: boolean;
constructor (
isNegative: boolean,
value: AnyNumber = 0,
bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = true) {
super(
AbstractInt.decodeAbstracInt(value, bitLength, isNegative)
);
this._bitLength = bitLength;
this._isHexJson = isHexJson;
this._isNegative = isNegative;
}
static decodeAbstracInt (value: AnyNumber, bitLength: UIntBitLength, isNegative: boolean): string {
// This function returns a string, which will be passed in the BN
// constructor. It would be ideal to actually return a BN, but there's a
// bug: https://github.com/indutny/bn.js/issues/206.
if (isHex(value)) {
return hexToBn(value, { isLe: false, isNegative }).toString();
} else if (isU8a(value)) {
try {
// NOTE When passing u8a in (typically from decoded data), it is always LE
return u8aToBn(value.subarray(0, bitLength / 8), { isLe: true, isNegative }).toString();
} catch (error) {
console.error(`AbstractInt value decoding failed ${error.message}`, value);
return '0';
}
} else if (isString(value)) {
return new BN(value, 10).toString();
}
return bnToBn(value).toString();
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return this._bitLength / 8;
}
/**
* @description Checks if the value is a zero value (align elsewhere)
*/
get isEmpty (): boolean {
return this.isZero();
}
/**
* @description Returns the number of bits in the value
*/
bitLength (): UIntBitLength {
return this._bitLength;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
// Here we are actually overriding the built-in .eq to take care of both
// number and BN inputs (no `.eqn` needed) - numbers will be converted
return super.eq(
isHex(other)
? hexToBn(other.toString(), { isLe: false, isNegative: this._isNegative })
: bnToBn(other)
);
}
/**
* @description Returns the BN representation of the number. (Compatibility)
*/
toBn (): BN {
return this;
}
/**
* @description Returns a hex string representation of the value
*/
abstract toHex (): string;
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
// Maximum allowed integer for JS is 2^53 - 1, set limit at 52
return this._isHexJson || (super.bitLength() > 52)
? this.toHex()
: this.toNumber();
}
/**
* @description Returns the string representation of the value
* @param base The base to use for the conversion
*/
toString (base?: number): string {
// only included here since we do not inherit docs
return super.toString(base);
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
abstract toU8a (isBare?: boolean): Uint8Array;
}
+15
View File
@@ -0,0 +1,15 @@
// 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.
/**
* @name Base
* @description A type extends the Base class, when it holds a value
*/
export default class Base<T = any> {
protected raw: T;
constructor (value?: any) {
this.raw = value;
}
}
+161
View File
@@ -0,0 +1,161 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import Moment from '../primitive/Moment';
import U32 from '../primitive/U32';
import Balance from '../type/Balance';
import BlockNumber from '../type/BlockNumber';
import Compact from './Compact';
import UInt from './UInt';
describe('Compact', () => {
describe('encodeU8a', () => {
it('encodes short u8', () => {
expect(
Compact.encodeU8a(18)
).toEqual(
new Uint8Array([18 << 2])
);
});
it('encodes max u8 values', () => {
expect(
Compact.encodeU8a(new UInt(63))
).toEqual(
new Uint8Array([0b11111100])
);
});
it('encodes basic u16 value', () => {
expect(
Compact.encodeU8a(511)
).toEqual(
new Uint8Array([0b11111101, 0b00000111])
);
});
it('encodes basic ua6 (not at edge)', () => {
expect(
Compact.encodeU8a(111)
).toEqual(
new Uint8Array([0xbd, 0x01])
);
});
it('encodes basic u32 values (short)', () => {
expect(
Compact.encodeU8a(0xffff)
).toEqual(
new Uint8Array([254, 255, 3, 0])
);
});
it('encodes basic u32 values (full)', () => {
expect(
Compact.encodeU8a(0xfffffff9)
).toEqual(
new Uint8Array([3, 249, 255, 255, 255])
);
});
it('encondes a large balance', () => {
expect(
Compact.encodeU8a(new Balance('0x5af3107a4000'))
).toEqual(
new Uint8Array([
3 + ((6 - 4) << 2),
0x00, 0x40, 0x7a, 0x10, 0xf3, 0x5a
])
);
});
});
describe('decodeU8a', () => {
it('decoded u8 value', () => {
expect(
Compact.decodeU8a(new Uint8Array([0b11111100]), 32)
).toEqual([1, new BN(63)]);
});
it('decodes from same u16 encoded value', () => {
expect(
Compact.decodeU8a(new Uint8Array([0b11111101, 0b00000111]), 32)
).toEqual([2, new BN(511)]);
});
it('decodes from same u32 encoded value (short)', () => {
expect(
Compact.decodeU8a(new Uint8Array([254, 255, 3, 0]), 32)
).toEqual([4, new BN(0xffff)]);
});
it('decodes from same u32 encoded value (full)', () => {
expect(
Compact.decodeU8a(new Uint8Array([3, 249, 255, 255, 255]), 32)
).toEqual([5, new BN(0xfffffff9)]);
});
it('decodes from same u32 as u64 encoded value (full, default)', () => {
expect(
Compact.decodeU8a(new Uint8Array([3, 249, 255, 255, 255]), 64)
).toEqual([5, new BN(0xfffffff9)]);
});
});
describe('constructor', () => {
it('has the correct bitLength for constructor values (BlockNumber)', () => {
expect(
new Compact(BlockNumber, 0xfffffff9).bitLength()
).toEqual(64);
});
it('has the correct encodedLength for constructor values (BlockNumber)', () => {
expect(
new Compact(BlockNumber, 0xfffffff9).encodedLength
).toEqual(5);
});
it('has the correct encodedLength for constructor values (u32)', () => {
expect(
new Compact(U32, 0xffff9).encodedLength
).toEqual(4);
});
it('constructs properly via U8a as U32', () => {
expect(
new Compact(U32, new Uint8Array([254, 255, 3, 0])).toNumber()
).toEqual(new BN(0xffff).toNumber());
});
it('constructs properly via number as Moment', () => {
expect(
new Compact(Moment, 1537968546).toString().startsWith('Wed Sep 26 2018') // The time depends on the timezone this test is run in
).toBe(true);
});
});
describe('utils', () => {
it('compares against another Compact', () => {
expect(new Compact(U32, 12345).eq(new Compact(U32, 12345))).toBe(true);
});
it('compares against a primitive', () => {
expect(new Compact(U32, 12345).eq(12345)).toBe(true);
});
it('unwraps to the wrapped value', () => {
expect(new Compact(U32, 12345).unwrap() instanceof U32).toBe(true);
});
});
describe('helpers', () => {
it('correctly adds the length prefix', () => {
expect(
Compact.addLengthPrefix(Uint8Array.from([12, 13]))
).toEqual(Uint8Array.from([2 << 2, 12, 13]));
});
});
});
+150
View File
@@ -0,0 +1,150 @@
// 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 BN from 'bn.js';
import { bnToBn, compactAddLength, compactFromU8a, compactStripLength, compactToU8a, hexToBn, isBn, isHex, isNumber, isString } from '@plugnet/util';
import { DEFAULT_BITLENGTH } from '@plugnet/util/compact/defaults';
import Moment from '../primitive/Moment';
import { AnyNumber, Codec, Constructor } from '../types';
import { UIntBitLength } from './AbstractInt';
import Base from './Base';
import UInt from './UInt';
/**
* @name Compact
* @description
* A compact length-encoding codec wrapper. It performs the same function as Length, however
* differs in that it uses a variable number of bytes to do the actual encoding. This is mostly
* used by other types to add length-prefixed encoding, or in the case of wrapped types, taking
* a number and making the compact representation thereof
*/
export default class Compact extends Base<UInt | Moment> implements Codec {
constructor (Type: Constructor<UInt | Moment>, value: Compact | AnyNumber = 0) {
super(Compact.decodeCompact(Type, value));
}
static with (Type: Constructor<UInt | Moment>): Constructor<Compact> {
return class extends Compact {
constructor (value?: any) {
super(Type, value);
}
};
}
/**
* Prepend a Uint8Array with its compact length.
*
* @param u8a - The Uint8Array to be prefixed
*/
static addLengthPrefix = compactAddLength;
static decodeU8a = compactFromU8a;
static encodeU8a = compactToU8a;
static stripLengthPrefix (u8a: Uint8Array, bitLength: UIntBitLength = DEFAULT_BITLENGTH): Uint8Array {
const [, value] = compactStripLength(u8a, bitLength);
return value;
}
static decodeCompact (Type: Constructor<UInt | Moment>, value: Compact | AnyNumber): Moment | UInt {
if (value instanceof Compact) {
return new Type(value.raw);
} else if (isString(value)) {
return new Type(
isHex(value, -1, true)
? hexToBn(value)
: new BN(value, 10)
);
} else if (isNumber(value) || isBn(value)) {
return new Type(bnToBn(value));
}
const [, _value] = Compact.decodeU8a(value, new Type(0).bitLength());
return new Type(_value);
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return this.toU8a().length;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.raw.isEmpty;
}
/**
* @description Returns the number of bits in the value
*/
bitLength (): UIntBitLength {
return this.raw.bitLength();
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
return this.raw.eq(
other instanceof Compact
? other.raw
: other
);
}
/**
* @description Returns the BN representation of the number
*/
toBn (): BN {
return this.raw.toBn();
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): any {
return this.raw.toHex();
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.raw.toJSON();
}
/**
* @description Returns the number representation for the value
*/
toNumber (): number {
return this.raw.toNumber();
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.raw.toString();
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return Compact.encodeU8a(this.raw.toBn());
}
/**
* @description Returns the embedded [[UInt]] or [[Moment]] value
*/
unwrap (): UInt | Moment {
return this.raw;
}
}
+83
View File
@@ -0,0 +1,83 @@
// 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 Enum from './Enum';
const testDecode = (type: string, input: any, expected: any) =>
it(`can decode from ${type}`, () => {
const e = new Enum(['foo', 'bar'], input);
expect(e.toString()).toBe(expected);
});
const testEncode = (to: 'toJSON' | 'toNumber' | 'toString' | 'toU8a', expected: any) =>
it(`can encode ${to}`, () => {
const e = new Enum(['foo', 'bar'], 1);
expect(e[to]()).toEqual(expected);
});
describe('Enum', () => {
testDecode('Enum', undefined, 'foo');
testDecode('Enum', new Enum([], 1), 'bar');
testDecode('number', 0, 'foo');
testDecode('number', 1, 'bar');
testDecode('string', 'bar', 'bar');
testDecode('Uint8Array', Uint8Array.from([0]), 'foo');
testDecode('Uint8Array', Uint8Array.from([1]), 'bar');
testEncode('toJSON', 1);
testEncode('toNumber', 1);
testEncode('toString', 'bar');
testEncode('toU8a', Uint8Array.from([1]));
it('provides a clean toString()', () => {
expect(
new Enum(['foo', 'bar']).toString()
).toEqual('foo');
});
it('provides a clean toString() (enum)', () => {
expect(
new Enum(['foo', 'bar'], new Enum([], 1)).toNumber()
).toEqual(1);
});
it('converts to and from U8a', () => {
expect(
new Enum(['foo', 'bar'], new Uint8Array([1])).toU8a()
).toEqual(new Uint8Array([1]));
});
it('converts from JSON', () => {
expect(
new Enum(['foo', 'bar'], 5).toString()
).toEqual('5');
});
describe('utils', () => {
it('compares agains the index value', () => {
expect(
new Enum(['foo', 'bar'], 1).eq(1)
).toBe(true);
});
it('compares agains the index value (false)', () => {
expect(
new Enum(['foo', 'bar'], 1).eq(0)
).toBe(false);
});
it('compares agains the string value', () => {
expect(
new Enum(['foo', 'bar'], 1).eq('bar')
).toBe(true);
});
it('compares agains the string value (false)', () => {
expect(
new Enum(['foo', 'bar'], 1).eq('foo')
).toBe(false);
});
});
});
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { isString, isU8a, u8aToHex, assert } from '@plugnet/util';
import { Codec } from '../types';
import Base from './Base';
type EnumMap = {
[index: number]: string
};
type EnumDef = {
[index: string]: number
} | Array<string>;
/**
* @name Enum
* @description
* A codec wrapper for an enum. Enums are encoded as a single byte, where the byte
* is a zero-indexed value. This class allows you to retrieve the value either
* by `toNumber()` exposing the actual raw index, or `toString()` returning a
* string representation (as provided as part of the constructor)
*/
// TODO:
// - It would be great if this could actually wrap actual TS enums
export default class Enum extends Base<number> implements Codec {
private _enum: EnumMap | Array<string>;
constructor (def: EnumDef, value: Enum | Uint8Array | string | number = 0) {
const decoded = Enum.decodeEnum(def, value);
assert(decoded !== -1, `Unable to initialise Enum with value ${value}`);
super(decoded);
this._enum = Array.isArray(def)
? def
: Object.keys(def).reduce((result, key) => {
result[def[key]] = key;
return result;
}, {} as EnumMap);
}
static decodeEnum (def: EnumDef, value: Enum | Uint8Array | string | number): number | undefined {
if (value instanceof Enum) {
return value.raw;
} else if (isU8a(value)) {
return value[0];
} else if (isString(value)) {
return Array.isArray(def)
? def.indexOf(value)
: def[value] || -1;
}
return value;
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return 1;
}
/**
* @description Returns the index for this value (equivalent to toNumber)
*/
get index (): number {
return this.raw;
}
/**
* @description Checks if the value is an empty value (always false)
*/
get isEmpty (): boolean {
return false;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
if (other instanceof Enum) {
return this.raw === other.raw;
}
return isString(other)
? this.toString() === other
: this.raw === other;
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.raw;
}
/**
* @description Returns the number representation for the value
*/
toNumber (): number {
return this.raw;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this._enum[this.raw] || `${this.raw}`;
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return new Uint8Array([this.raw]);
}
}
+133
View File
@@ -0,0 +1,133 @@
// 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 EnumType from './EnumType';
import Null from '../primitive/Null';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
describe('Struct', () => {
it('provides a clean toString() (value)', () => {
expect(
new EnumType(
{ Text, U32 },
new Uint8Array([0, 2 << 2, 49, 50])
).value.toString()
).toEqual('12');
});
it('provides a clean toString() (enum)', () => {
expect(
new EnumType(
{ Text, U32 },
new Uint8Array([1, 2 << 2, 49, 50])
).toString()
).toEqual('{"U32":3289352}');
});
it('decodes from a JSON input (lowercase)', () => {
expect(
new EnumType(
{ Text, U32 },
{ 'text': 'some text value' }
).value.toString()
).toEqual('some text value');
});
it('decodes from hex', () => {
expect(
new EnumType(
{ Text, U32 },
'0x0134120000'
).value.toString()
).toEqual('4660'); // 0x1234 in decimal
});
it('decodes from a JSON input (mixed case)', () => {
expect(
new EnumType(
{ Text, U32 },
{ 'U32': 42 }
).value.toString()
).toEqual('42');
});
it('decodes from JSON string', () => {
expect(
new EnumType(
{ Null, U32 },
'null'
).type
).toEqual('Null');
});
it('stringifies with custom types', () => {
class A extends Null { }
class B extends Null { }
class C extends Null { }
class Test extends EnumType<A | B | C> {
constructor (value?: string, index?: number) {
super({
a: A,
b: B,
c: C
}, value, index);
}
}
expect(new Test().toJSON()).toEqual({ a: null });
});
it('creates via with', () => {
class A extends Null { }
class B extends U32 { }
class C extends Null { }
const Test = EnumType.with({ A, B, C });
expect(new Test().toJSON()).toEqual({ A: null });
expect(new Test(1234, 1).toJSON()).toEqual({ B: 1234 });
});
// We are currently not using this approach, none of the types in Substrate currently
// have any overrides. Insteda of trying to support it (just-in-case), rather have it
// removed to simplyfy the code - it can be pulled-back if needed
it.skip('allows checking against defined indexes', () => {
expect(
new EnumType(
{ 1: Text, 5: U32 },
new Uint8Array([1, 2 << 2, 49, 50])
).toString()
).toEqual('Text');
});
it('allows accessing the type and value', () => {
const text = new Text('foo');
const enumType = new EnumType(
{ Text, U32 },
{ Text: text }
);
expect(enumType.type).toBe('Text');
expect(enumType.value).toEqual(text);
});
describe('utils', () => {
const eqtest = new EnumType(
{ U32, Text },
new Uint8Array([1, 3 << 2, 88, 89, 90])
);
it('compares against index', () => {
expect(
eqtest.eq(1)
).toBe(true);
});
it('compares against values', () => {
expect(
eqtest.eq('XYZ')
).toBe(true);
});
});
});
+229
View File
@@ -0,0 +1,229 @@
// 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, ConstructorDef } from '../types';
import { assert, hexToU8a, isHex, isNumber, isObject, isString, isU8a, u8aConcat, u8aToHex } from '@plugnet/util';
import Null from '../primitive/Null';
import Base from './Base';
type EnumConstructor<T = Codec> = { new(value?: any, index?: number): T };
type TypesDef = {
[name: string]: Constructor
};
type Decoded = {
index: number,
value: Codec
};
type Aliasses = {
[index: string]: string
};
/**
* @name EnumType
* @description
* This implements an enum, that based on the value wraps a different type. It is effectively
* an extension to enum where the value type is determined by the actual index.
*/
// TODO:
// - As per Enum, actually use TS enum
// - It should rather probably extend Enum instead of copying code
export default class EnumType<T> extends Base<Codec> implements Codec {
private _def: TypesDef;
private _index: number;
private _indexes: Array<number>;
constructor (def: TypesDef, value?: any, index?: number | EnumType<T>, aliasses?: Aliasses) {
const decoded = EnumType.decodeEnumType(def, aliasses || {}, value, index);
super(decoded.value);
this._def = def;
this._indexes = Object.keys(def).map((_, index) => index);
this._index = this._indexes.indexOf(decoded.index) || 0;
}
private static decodeEnumType<T> (def: TypesDef, aliasses: Aliasses, value?: any, index?: number | EnumType<T>): Decoded {
// If `index` is set, we parse it.
if (index instanceof EnumType) {
return EnumType.createValue(def, index._index, index.raw);
} else if (isNumber(index)) {
return EnumType.createValue(def, index, value);
}
// Or else, we just look at `value`
return EnumType.decodeViaValue(def, aliasses, value);
}
private static decodeViaValue (def: TypesDef, aliasses: Aliasses, value?: any): Decoded {
if (value instanceof EnumType) {
return EnumType.createValue(def, value._index, value.raw);
} else if (isU8a(value)) {
return EnumType.createValue(def, value[0], value.subarray(1));
} else if (isNumber(value)) {
return EnumType.createValue(def, value);
} else if (isString(value)) {
const _str = value.toString();
return isHex(_str)
? EnumType.decodeViaValue(def, aliasses, hexToU8a(_str))
: EnumType.createViaJSON(def, aliasses, _str);
} else if (isObject(value)) {
const key = Object.keys(value)[0];
return EnumType.createViaJSON(def, aliasses, key, value[key]);
}
// Worst-case scenario, return the first with default
return EnumType.createValue(def, 0);
}
private static createViaJSON (def: TypesDef, _aliasses: Aliasses, key: string, value?: any) {
// JSON comes in the form of { "<type (lowercased)>": "<value for type>" }, here we
// additionally force to lower to ensure forward compat
const keys = Object.keys(def).map((k) => k.toLowerCase());
// FIXME This whole lookup process is not vey efficient
const keyLower = key.toLowerCase();
const aliasses = Object.keys(_aliasses).reduce((aliasses, a) => {
aliasses[a.toLowerCase()] = _aliasses[a].toLowerCase();
return aliasses;
}, {} as Aliasses);
const aliasKey = aliasses[keyLower] || keyLower;
const index = keys.indexOf(aliasKey);
assert(index !== -1, `Cannot map input on JSON, unable to find '${key}' in ${keys.join(', ')}`);
return EnumType.createValue(def, index, value);
}
private static createValue (def: TypesDef, index: number = 0, value?: any): Decoded {
return {
index,
value: new (Object.values(def)[index])(value)
};
}
static with<
S extends ConstructorDef
> (Types: S): EnumConstructor<EnumType<S>> {
return class extends EnumType<S> {
constructor (value?: any, index?: number) {
super(Types, value, index);
}
};
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return 1 + this.raw.encodedLength;
}
/**
* @description The index of the metadata value
*/
get index (): number {
return this._index;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.isEmpty;
}
/**
* @description Checks if the Enum points to a [[Null]] type
*/
get isNone (): boolean {
return this.isNull;
}
/**
* @description Checks if the Enum points to a [[Null]] type (deprecated, use isNone)
*/
get isNull (): boolean {
return this.raw instanceof Null;
}
/**
* @description The name of the type this enum value represents
*/
get type (): string {
return Object.keys(this._def)[this._index];
}
/**
* @description The value of the enum
*/
get value (): Codec {
return this.raw;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
// cater for the case where we only pass the enum index
if (isNumber(other)) {
return this.toNumber() === other;
}
// compare the actual wrapper value
return this.value.eq(other);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return {
[this.type]: this.raw.toJSON()
};
}
/**
* @description Returns the number representation for the value
*/
toNumber (): number {
return this._index;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.isNull
? this.type
: JSON.stringify(this.toJSON());
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
const index = this._indexes[this._index];
return u8aConcat(
new Uint8Array([index]),
this.raw.toU8a(isBare)
);
}
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import Int from './Int';
describe('Int', () => {
it('provides a toBn interface', () => {
expect(
new Int(-1234).toBn().toNumber()
).toEqual(-1234);
});
it('provides a toNumber interface', () => {
expect(
new Int(-1234).toNumber()
).toEqual(-1234);
});
it('converts to LE from the provided value', () => {
expect(
new Int(-1234).toU8a()
).toEqual(new Uint8Array([46, 251, 255, 255, 255, 255, 255, 255]));
});
it('converts to LE from the provided value (bitLength)', () => {
expect(
new Int(-1234, 32).toU8a()
).toEqual(new Uint8Array([46, 251, 255, 255]));
});
it('converts to hex/string', () => {
const i = new Int('0x12', 16);
expect(i.toHex()).toEqual('0x0012');
expect(i.toString()).toEqual('18');
});
it('converts to equivalents', () => {
const a = new Int('-123');
expect(
new Int(a).toNumber()
).toEqual(-123);
});
it('converts to JSON depending on flags', () => {
expect(new Int(0x12, 16).toJSON()).toEqual('0x0012');
expect(new Int(0x12, 16, false).toJSON()).toEqual(0x12);
});
});
+55
View File
@@ -0,0 +1,55 @@
// 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 { AnyNumber } from '../types';
import { bnToHex, bnToU8a } from '@plugnet/util';
import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt';
/**
* @name Int
* @description
* A generic signed integer codec. For Substrate all numbers are LE encoded,
* this handles the encoding and decoding of those numbers. Upon construction
* the bitLength is provided and any additional use keeps the number to this
* length. This extends `BN`, so all methods available on a normal `BN` object
* is available here.
* @noInheritDoc
*/
export default class Int extends AbstractInt {
constructor (
value: AnyNumber = 0,
bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = true) {
super(
true,
value,
bitLength,
isHexJson
);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return bnToHex(this, {
bitLength: this._bitLength,
isLe: false,
isNegative: true
});
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return bnToU8a(this, {
bitLength: this._bitLength,
isLe: true,
isNegative: true
});
}
}
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Struct, Option, Tuple, Vector } from '.';
import { Constructor, Codec } from '../types';
type TypeWithValues = [Constructor, Array<any>];
export class Linkage<T extends Codec> extends Struct {
constructor (Type: Constructor, value?: any) {
super({
previous: Option.with(Type),
next: Option.with(Type)
}, value);
}
static withKey<O extends Codec> (Type: Constructor): Constructor<Linkage<O>> {
return class extends Linkage<O> {
constructor (value?: any) {
super(Type, value);
}
};
}
get previous (): Option<T> {
return this.get('previous') as Option<T>;
}
get next (): Option<T> {
return this.get('next') as Option<T>;
}
}
export class LinkageResult extends Tuple {
constructor ([TypeKey, keys]: TypeWithValues, [TypeValue, values]: TypeWithValues) {
super({
Keys: Vector.with(TypeKey),
Values: Vector.with(TypeValue)
}, [keys, values]);
}
}
+88
View File
@@ -0,0 +1,88 @@
// 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 Option from './Option';
import Text from '../primitive/Text';
const testDecode = (type: string, input: any, expected: any) =>
it(`can decode from ${type}`, () => {
const o = new Option(Text, input);
expect(o.toString()).toBe(expected);
expect(o.isNone).toBe(!expected.length);
});
const testEncode = (to: string, expected: any) =>
it(`can encode ${to}`, () => {
const e = new Option(Text, 'foo');
expect((e as any)[to]()).toEqual(expected);
});
describe('Option', () => {
it('converts undefined/null to empty', () => {
expect(new Option(Text, undefined).isNone).toBe(true);
expect(new Option(Text, null).isNone).toBe(true);
expect(new Option(Text, 'test').isNone).toBe(false);
});
it('converts an option to an option', () => {
expect(
new Option(Text, new Option(Text, 'hello')).toString()
).toEqual('hello');
});
testDecode('string (with)', 'foo', 'foo');
testDecode('string (without)', undefined, '');
testDecode('Uint8Array (with)', Uint8Array.from([1, 12, 102, 111, 111]), 'foo');
testDecode('Uint8Array (without)', Uint8Array.from([0]), '');
testEncode('toHex', '0x010c666f6f');
testEncode('toString', 'foo');
testEncode('toU8a', Uint8Array.from([1, 12, 102, 111, 111]));
it('has empty toString() (undefined)', () => {
expect(
new Option(Text).toString()
).toEqual('');
});
it('has value toString() (provided)', () => {
expect(
new Option(Text, new Uint8Array([1, 4 << 2, 49, 50, 51, 52])).toString()
).toEqual('1234');
});
it('converts toU8a() with', () => {
expect(
new Option(Text, '1234').toU8a()
).toEqual(new Uint8Array([1, 4 << 2, 49, 50, 51, 52]));
});
it('converts toU8a() without', () => {
expect(
new Option(Text).toU8a()
).toEqual(new Uint8Array([0]));
});
describe('utils', () => {
const testeq = new Option(Text, '1234');
it('compares against other option', () => {
expect(testeq.eq(new Option(Text, '1234'))).toBe(true);
});
it('compares against raw value', () => {
expect(testeq.eq('1234')).toBe(true);
});
it('unwraps to default if empty', () => {
expect(new Option(Text).unwrapOr('6789')).toBe('6789');
});
it('unwraps to value if non-empty', () => {
expect((new Option(Text, '1234').unwrapOr(null) as Text).toString()).toBe('1234');
});
});
});
+160
View File
@@ -0,0 +1,160 @@
// 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 { isNull, isU8a, isUndefined, u8aToHex } from '@plugnet/util';
import Base from './Base';
import { Codec, Constructor } from '../types';
import Null from '../primitive/Null';
/**
* @name Option
* @description
* An Option is an optional field. Basically the first byte indicates that there is
* is value to follow. If the byte is `1` there is an actual value. So the Option
* implements that - decodes, checks for optionality and wraps the required structure
* with a value if/as required/found.
*/
export default class Option<T extends Codec> extends Base<T> implements Codec {
constructor (Type: Constructor, value?: any) {
super(
Option.decodeOption(Type, value)
);
}
static decodeOption<O> (Type: Constructor, value?: any): Codec {
if (isNull(value) || isUndefined(value) || value instanceof Null) {
return new Null();
} else if (value instanceof Option) {
return Option.decodeOption(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 value[0] === 0
? new Null()
: new Type(value.subarray(1));
}
return new Type(value);
}
static with<O extends Codec> (Type: Constructor): Constructor<Option<O>> {
return class extends Option<O> {
constructor (value?: any) {
super(Type, value);
}
};
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
// boolean byte (has value, doesn't have) along with wrapped length
return 1 + this.raw.encodedLength;
}
/**
* @description Checks if the Option has no value
*/
get isEmpty (): boolean {
return this.isNone;
}
/**
* @description Checks if the Option has no value
*/
get isNone (): boolean {
return this.raw instanceof Null;
}
/**
* @description Checks if the Option has a value
*/
get isSome (): boolean {
return !this.isNone;
}
/**
* @description The actual value for the Option
*/
get value (): Codec {
return this.raw;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
if (other instanceof Option) {
return (this.isSome === other.isSome) && this.value.eq(other.value);
}
return this.value.eq(other);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.raw.toJSON();
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.raw.toString();
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
if (isBare) {
return this.raw.toU8a(true);
}
const u8a = new Uint8Array(this.encodedLength);
if (this.isSome) {
u8a.set([1]);
u8a.set(this.raw.toU8a(), 1);
}
return u8a;
}
/**
* @description Returns the value that the Option represents (if available), throws if null
*/
unwrap (): T {
if (this.isNone) {
throw new Error('Option: unwrapping a None value');
}
return this.raw;
}
/**
* @description Returns the value that the Option represents (if available) or defaultValue if none
* @param defaultValue The value to return if the option isNone
*/
unwrapOr <O> (defaultValue: O): T | O {
return this.isSome
? this.unwrap()
: defaultValue;
}
}
+73
View File
@@ -0,0 +1,73 @@
// 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 Set from './Set';
const SET_FIELDS = {
header: 0b00000001,
body: 0b00000010,
receipt: 0b00000100,
messageQueue: 0b00001000,
justification: 0b00010000
};
const SET_ROLES = {
none: 0b00000000,
full: 0b00000001,
light: 0b00000010,
authority: 0b00000100
};
describe('Set', () => {
it('constructs via an Array<string>', () => {
const set = new Set(SET_ROLES, ['full', 'authority', 'invalid']);
expect(set.isEmpty).toEqual(false);
expect(set.toString()).toEqual(
'[full, authority]'
);
});
it('constructs via Uint8Array', () => {
const set = new Set(SET_FIELDS, new Uint8Array([0b00000001 | 0b00000010 | 0b00010000 | 0b10000000]));
expect(set.encodedLength).toEqual(1);
expect(set.toJSON()).toEqual([
'header', 'body', 'justification'
]);
});
it('hash a valid encoding', () => {
const set = new Set(SET_FIELDS, ['header', 'body', 'justification']);
expect(set.toU8a()).toEqual(new Uint8Array([19]));
});
describe('utils', () => {
const set = new Set(SET_ROLES, ['full', 'authority']);
it('compares against string array', () => {
expect(
set.eq(['authority', 'full'])
).toBe(true);
});
it('compares against number (encoded)', () => {
expect(
set.eq(SET_ROLES.full | SET_ROLES.authority)
).toBe(true);
});
it('compares against other sets', () => {
expect(
set.eq(new Set(SET_ROLES, ['authority', 'full']))
).toBe(true);
});
it('returns false on other values', () => {
expect(
set.eq('full')
).toBe(false);
});
});
});
+142
View File
@@ -0,0 +1,142 @@
// 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 { isU8a, isNumber, isUndefined, u8aToHex } from '@plugnet/util';
import Base from './Base';
import { Codec } from '../types';
import { compareArray } from './utils';
type SetValues = {
[index: string]: number
};
/**
* @name Set
* @description
* An Set is an array of string values, represented an an encoded type by
* a bitwise representation of the values.
*/
// FIXME This is a prime candidate to extend the JavaScript built-in Set
export default class Set extends Base<Array<string>> implements Codec {
private _setValues: SetValues;
constructor (setValues: SetValues, value?: Array<string> | Uint8Array | number) {
super(
Set.decodeSet(setValues, value)
);
this._setValues = setValues;
}
static decodeSet (setValues: SetValues, value: Array<string> | Uint8Array | number = 0): Array<string> {
if (isU8a(value)) {
return Set.decodeSet(setValues, value[0]);
} else if (Array.isArray(value)) {
return value.reduce((result, value) => {
if (isUndefined(setValues[value])) {
console.error(`Ignoring invalid '${value}' passed to Set`);
} else {
result.push(value);
}
return result;
}, [] as Array<string>);
}
const result = Object.keys(setValues).reduce((result, key) => {
if ((value & setValues[key]) === setValues[key]) {
result.push(key);
}
return result;
}, [] as Array<string>);
const computed = Set.encodeSet(setValues, result);
if (value !== computed) {
console.error(`Mismatch decoding '${value}', computed as '${computed}' with ${result}`);
}
return result;
}
static encodeSet (setValues: SetValues, value: Array<string>): number {
return value.reduce((result, value) => {
return result | (setValues[value] || 0);
}, 0);
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return 1;
}
/**
* @description true is the Set contains no values
*/
get isEmpty (): boolean {
return this.values.length === 0;
}
/**
* @description The actual set values as a Array<string>
*/
get values (): Array<string> {
return this.raw;
}
/**
* @description The encoded value for the set members
*/
get valueEncoded (): number {
return Set.encodeSet(this._setValues, this.raw);
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
if (Array.isArray(other)) {
// we don't actually care about the order, sort the values
return compareArray(this.values.sort(), other.sort());
} else if (other instanceof Set) {
return this.eq(other.values);
} else if (isNumber(other)) {
return this.valueEncoded === other;
}
return false;
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.values;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return `[${this.values.join(', ')}]`;
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return new Uint8Array([this.valueEncoded]);
}
}
+186
View File
@@ -0,0 +1,186 @@
// 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 Text from '../primitive/Text';
import U32 from '../primitive/U32';
import BlockNumber from '../type/BlockNumber';
import { CodecTo } from '../types';
import Option from './Option';
import Struct from './Struct';
import Vector from './Vector';
describe('Struct', () => {
describe('decoding', () => {
const testDecode = (type: string, input: any) =>
it(`can decode from ${type}`, () => {
const s = new Struct({
foo: Text,
bar: U32
}, input);
expect([...s.keys()]).toEqual(['foo', 'bar']);
expect(
[...s.values()].map((v) =>
v.toString()
)
).toEqual(['bazzing', '69']);
});
testDecode('array', ['bazzing', 69]);
testDecode('hex', '0x1c62617a7a696e6745000000');
testDecode('object', { foo: 'bazzing', bar: 69 });
testDecode('Uint8Array', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]));
});
describe('encoding', () => {
const testEncode = (to: CodecTo, expected: any) =>
it(`can encode ${to}`, () => {
const s = new Struct({
foo: Text,
bar: U32
}, { foo: 'bazzing', bar: 69 });
expect(s[to]()).toEqual(expected);
});
testEncode('toHex', '0x1c62617a7a696e6745000000');
testEncode('toJSON', { foo: 'bazzing', bar: 69 });
testEncode('toU8a', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]));
testEncode('toString', '{"foo":"bazzing","bar":69}');
});
it('decodes null', () => {
expect(
new (
Struct.with({
txt: Text,
u32: U32
})
)(null).toString()
).toEqual('{}');
});
it('decodes a more complicated type', () => {
const s = new Struct({
foo: Vector.with(Struct.with({
bar: Text
}))
}, { foo: [{ bar: 1 }, { bar: 2 }] });
expect(s.toString()).toBe('{"foo":[{"bar":"1"},{"bar":"2"}]}');
});
it('decodes from a Map input', () => {
const input = new Struct({
a: U32,
txt: Text
}, { a: 42, txt: 'fubar' });
const s = new Struct({
txt: Text,
foo: U32,
bar: U32
}, input);
expect(s.toString()).toEqual('{"txt":"fubar","foo":0,"bar":0}');
});
it('throws when it cannot decode', () => {
expect(
() => new (
Struct.with({
txt: Text,
u32: U32
})
)('ABC')
).toThrowError(/Struct: cannot decode type/);
});
it('provides a clean toString()', () => {
expect(
new (
Struct.with({
txt: Text,
u32: U32
})
)({ txt: 'foo', u32: 0x123456 }).toString()
).toEqual('{"txt":"foo","u32":1193046}');
});
it('exposes the properties on the object', () => {
const struct = new (
Struct.with({
txt: Text,
u32: U32
})
)({ txt: 'foo', u32: 0x123456 });
expect((struct as any).txt.toString()).toEqual('foo');
expect((struct as any).u32.toNumber()).toEqual(0x123456);
});
it('correctly encodes length', () => {
expect(
new (
Struct.with({
txt: Text,
u32: U32
})
)({ foo: 'bazzing', bar: 69 }).encodedLength
).toEqual(5);
});
it('exposes the types', () => {
expect(
new Struct({
foo: Text,
bar: Text,
baz: U32
}, {
foo: 'foo',
bar: 'bar',
baz: 3
}).Type
).toEqual({
foo: 'Text',
bar: 'Text',
baz: 'U32'
});
});
it('gets the value at a particular index', () => {
expect(
new (
Struct.with({
txt: Text,
u32: U32
})
)({ txt: 'foo', u32: 1234 })
.getAtIndex(1)
.toString()
).toEqual('1234');
});
describe('utils', () => {
it('compares against other objects', () => {
const test = {
foo: 'foo',
bar: 'bar',
baz: 3
};
expect(
new Struct({
foo: Text,
bar: Text,
baz: U32
}, test).eq(test)
).toBe(true);
});
});
it('allows toString with large numbers', () => {
// replicate https://github.com/polkadot-js/api/issues/640
expect(
new Struct({
blockNumber: Option.with(BlockNumber)
}, { blockNumber: '0x1234567890abcdef' }).toString()
).toEqual('{"blockNumber":"0x1234567890abcdef"}');
});
});
+241
View File
@@ -0,0 +1,241 @@
// 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 { hexToU8a, isHex, isObject, isU8a, u8aConcat, u8aToHex } from '@plugnet/util';
import { Codec, Constructor, ConstructorDef } from '../types';
import { compareMap, decodeU8a } from './utils';
/**
* @name Struct
* @description
* A Struct defines an Object with key/values - where the values are Codec values. It removes
* a lot of repetition from the actual coding, define a structure type, pass it the key/Codec
* values in the constructor and it manages the decoding. It is important that the constructor
* values matches 100% to the order in th Rust code, i.e. don't go crazy and make it alphabetical,
* it needs to decoded in the specific defined order.
* @noInheritDoc
*/
export default class Struct<
// The actual Class structure, i.e. key -> Class
S extends ConstructorDef = ConstructorDef,
// internal type, instance of classes mapped by key
T extends { [K in keyof S]: Codec } = { [K in keyof S]: Codec },
// input values, mapped by key can be anything (construction)
V extends { [K in keyof S]: any } = { [K in keyof S]: any },
// type names, mapped by key, name of Class in S
E extends { [K in keyof S]: string } = { [K in keyof S]: string }
> extends Map<keyof S, Codec> implements Codec {
protected _jsonMap: Map<keyof S, string>;
protected _Types: E;
constructor (Types: S, value: V | Map<any, any> | Array<any> = {} as V, jsonMap: Map<keyof S, string> = new Map()) {
const decoded = Struct.decodeStruct<S, V, T>(Types, value, jsonMap);
super(
Object.entries(decoded)
);
this._jsonMap = jsonMap;
this._Types = (Object
.keys(Types) as Array<keyof S>)
.reduce((result: E, key) => {
// TS2322: Type 'string' is not assignable to type 'E[keyof S]'.
(result as any)[key] = Types[key].name;
return result;
}, {} as E);
}
/**
* Decode input to pass into constructor.
*
* @param Types - Types definition.
* @param value - Value to decode, one of:
* - null
* - undefined
* - hex
* - Uint8Array
* - object with `{ key1: value1, key2: value2 }`, assuming `key1` and `key2`
* are also keys in `Types`
* - array with `[value1, value2]` assuming the array has the same length as
* `Object.keys(Types)`
* @param jsonMap
*/
private static decodeStruct<
S extends ConstructorDef,
_,
T extends { [K in keyof S]: Codec }
> (Types: S, value: any, jsonMap: Map<keyof S, string>): T {
// l.debug(() => ['Struct.decode', { Types, value }]);
if (isHex(value)) {
return Struct.decodeStruct(Types, hexToU8a(value as string), jsonMap);
} else if (isU8a(value)) {
const values = decodeU8a(value, Object.values(Types));
// Transform array of values to {key: value} mapping
return Object.keys(Types).reduce((raw: T, key: keyof S, index) => {
// TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'.
(raw as any)[key] = values[index];
return raw;
}, {} as T);
} else if (!value) {
return {} as T;
}
// We assume from here that value is a JS object (Array, Map, Object)
return Struct.decodeStructFromObject(Types, value, jsonMap);
}
private static decodeStructFromObject<
S extends ConstructorDef,
_,
T extends { [K in keyof S]: Codec }
> (Types: S, value: any, jsonMap: Map<keyof S, string>): T {
return Object.keys(Types).reduce((raw: T, key: keyof S, index) => {
// The key in the JSON can be snake_case (or other cases), but in our
// Types, result or any other maps, it's camelCase
const jsonKey = (jsonMap.get(key as any) && !value[key]) ? jsonMap.get(key as any) : key;
if (Array.isArray(value)) {
raw[key] = value[index] instanceof Types[key]
? value[index]
: new Types[key](value[index]);
} else if (value instanceof Map) {
const mapped = value.get(jsonKey);
// TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'.
(raw as any)[key] = mapped instanceof Types[key]
? mapped
: new Types[key](mapped);
} else if (isObject(value)) {
raw[key] = value[jsonKey as string] instanceof Types[key]
? value[jsonKey as string]
: new Types[key](value[jsonKey as string]);
} else {
throw new Error(`Struct: cannot decode type ${Types[key].name} with value ${JSON.stringify(value)}`);
}
return raw;
}, {} as T);
}
static with<
S extends ConstructorDef
> (Types: S): Constructor<Struct<S>> {
return class extends Struct<S> {
constructor (value?: any, jsonMap?: Map<keyof S, string>) {
super(Types, value, jsonMap);
(Object.keys(Types) as Array<keyof S>).forEach((key) => {
Object.defineProperty(this, key, {
enumerable: true,
get: () => this.get(key)
});
});
}
};
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
const items = this.toArray();
for (let i = 0; i < items.length; i++) {
if (!items[i].isEmpty) {
return false;
}
}
return true;
}
/**
* @description Returns the Type description to sthe structure
*/
get Type (): E {
return this._Types;
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return this.toArray().reduce((length, entry) => {
return length += entry.encodedLength;
}, 0);
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
return compareMap(this, other);
}
/**
* @description Returns a specific names entry in the structure
* @param name The name of the entry to retrieve
*/
get (name: keyof S): Codec | undefined {
return super.get(name);
}
/**
* @description Returns the values of a member at a specific index (Rather use get(name) for performance)
*/
getAtIndex (index: number): Codec {
return this.toArray()[index];
}
/**
* @description Converts the Object to an standard JavaScript Array
*/
toArray (): Array<Codec> {
return [...this.values()];
}
/**
* @description Returns a hex string representation of the value
*/
toHex () {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return [...this.keys()].reduce((json, key) => {
const jsonKey = this._jsonMap.get(key) || key;
const value = this.get(key);
json[jsonKey] = value && value.toJSON();
return json;
}, {} as any);
}
/**
* @description Returns the string representation of the value
*/
toString () {
return JSON.stringify(this.toJSON());
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return u8aConcat(
...this.toArray().map((entry) =>
entry.toU8a(isBare)
)
);
}
}
+97
View File
@@ -0,0 +1,97 @@
// 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 extrinsics from '@plugnet/extrinsics/static';
import { CodecTo } from '../types';
import Method from '../primitive/Method';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
import BlockNumber from '../type/BlockNumber';
import Proposal from '../type/Proposal';
import VoteThreshold from '../type/VoteThreshold';
import Tuple from './Tuple';
describe('Tuple', () => {
let tuple: Tuple;
beforeEach(() => {
tuple = new Tuple(
[Text, U32],
['bazzing', 69]
);
});
describe('decoding', () => {
const testDecode = (type: string, input: any) =>
it(`can decode from ${type}`, () => {
const t = new Tuple([
Text,
U32
], input);
expect(
t.toJSON()
).toEqual(['bazzing', 69]);
});
testDecode('array', ['bazzing', 69]);
testDecode('hex', '0x1c62617a7a696e6745000000');
testDecode('Uint8Array', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]));
});
describe('encoding', () => {
const testEncode = (to: CodecTo | 'toArray', expected: any) =>
it(`can encode ${to}`, () => {
expect(tuple[to]()).toEqual(expected);
});
testEncode('toHex', '0x1c62617a7a696e6745000000');
testEncode('toJSON', ['bazzing', 69]);
testEncode('toU8a', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]));
testEncode('toString', '["bazzing",69]');
});
it.skip('creates properly via actual hex string', () => {
Method.injectMethods(extrinsics);
const test = new (Tuple.with([
BlockNumber, Proposal, VoteThreshold
]
))('0x62190000000000000003507b0a092230783432223a202230783433220a7d0a01');
expect((test[0] as BlockNumber).toNumber()).toEqual(6498);
expect((test[1] as Method).callIndex).toEqual(new Uint8Array([0, 3]));
expect((test[2] as VoteThreshold).toNumber()).toEqual(1);
});
it('exposes the Types', () => {
expect(tuple.Types).toEqual(['Text', 'U32']);
});
it('exposes the Types (object creation)', () => {
const test = new Tuple({
BlockNumber, VoteThreshold
}, []);
expect(test.Types).toEqual(['BlockNumber', 'VoteThreshold']);
});
it('exposes filter', () => {
expect(tuple.filter((v) => v.toJSON() === 69)).toEqual([new U32(69)]);
});
it('exposes map', () => {
expect(tuple.map(v => v.toString())).toEqual(['bazzing', '69']);
});
describe('utils', () => {
it('compares against inputs', () => {
expect(tuple.eq(['bazzing', 69])).toBe(true);
});
it('compares against inputs (mismatch)', () => {
expect(tuple.eq(['bazzing', 72])).toBe(false);
});
});
});
+152
View File
@@ -0,0 +1,152 @@
// 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 { isU8a, u8aConcat, u8aToHex, isHex, hexToU8a } from '@plugnet/util';
import { AnyNumber, AnyU8a, AnyString, Codec, Constructor } from '../types';
import { compareArray, decodeU8a } from './utils';
type TupleConstructors = Array<Constructor> | {
[index: string]: Constructor
};
/**
* @name Tuple
* @description
* A Tuple defines an anonymous fixed-length array, where each element has its
* own type. It extends the base JS `Array` object.
* @noInheritDoc
*/
export default class Tuple extends Array<Codec> implements Codec {
private _Types: TupleConstructors;
constructor (Types: TupleConstructors, value: any) {
super(
...Tuple.decodeTuple(Types, value)
);
this._Types = Types;
}
private static decodeTuple (_Types: TupleConstructors, value: AnyU8a | string | Array<AnyU8a | AnyNumber | AnyString | undefined | null>): Array<Codec> {
if (isU8a(value)) {
return decodeU8a(value, _Types);
} else if (isHex(value)) {
return Tuple.decodeTuple(_Types, hexToU8a(value));
}
const Types: Array<Constructor> = Array.isArray(_Types)
? _Types
: Object.values(_Types);
return Types.map((Type, index) => {
return new Type(value && value[index]);
});
}
static with (Types: TupleConstructors): Constructor<Tuple> {
return class extends Tuple {
constructor (value?: any) {
super(Types, value);
}
};
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return this.reduce((length, entry) => {
return length += entry.encodedLength;
}, 0);
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.length === 0;
}
/**
* @description The types definition of the tuple
*/
get Types (): Array<string> {
return Array.isArray(this._Types)
? this._Types.map(({ name }) => name)
: Object.keys(this._Types);
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
return compareArray(this, other);
}
/**
* @description Converts the Object to an standard JavaScript Array
*/
toArray (): Array<Codec> {
return Array.from(this);
}
/**
* @description Returns a hex string representation of the value
*/
toHex () {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.map((entry) =>
entry.toJSON()
);
}
/**
* @description Returns the string representation of the value
*/
toString () {
// Overwrite the default toString representation of Array.
return JSON.stringify(this.toJSON());
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return u8aConcat(
...this.map((entry) =>
entry.toU8a(isBare)
)
);
}
// Below are methods that we override. When we do a `new Tuple(...).map()`,
// we want it to throw an error. We only override the methods that return a
// new instance.
/**
* @description Filters the array with the callback
* @param callbackfn The filter function
* @param thisArg The `this` object to apply the result to
*/
filter (callbackfn: (value: Codec, index: number, array: Array<Codec>) => any, thisArg?: any): Array<Codec> {
return this.toArray().filter(callbackfn, thisArg);
}
/**
* @description Maps the array with the callback
* @param callbackfn The mapping function
* @param thisArg The `this` onject to apply the result to
*/
map<U> (callbackfn: (value: Codec, index: number, array: Array<Codec>) => U, thisArg?: any): Array<U> {
return this.toArray().map(callbackfn, thisArg);
}
}
@@ -0,0 +1,66 @@
// 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 { TypeRegistry } from './typeRegistry';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
describe('TypeRegistry', () => {
let registry: TypeRegistry;
beforeEach(() => {
registry = new TypeRegistry();
});
it('handles non exist type', () => {
expect(registry.get('non-exist')).toBeUndefined();
});
it('can register single type', () => {
registry.register(Text);
expect(registry.get('Text')).toBe(Text);
});
it('can register type with a different name', () => {
registry.register('TextRenamed', Text);
expect(registry.get('TextRenamed')).toBe(Text);
});
describe('object registration', () => {
it('can register multiple types', () => {
registry.register({
Text,
'U32Renamed': U32
});
expect(registry.get('Text')).toBe(Text);
expect(registry.get('U32Renamed')).toBe(U32);
});
it('can create types from string', () => {
registry.register({
'U32Renamed': 'u32'
});
expect(registry.get('U32Renamed')).toBe(U32);
});
it('can create structs via definition', () => {
registry.register({
'SomeStruct': {
'foo': 'u32',
'bar': 'Text'
}
});
const SomeStruct: any = registry.get('SomeStruct');
const struct: any = new SomeStruct({
foo: 42,
bar: 'testing'
});
expect(SomeStruct.name).toBe('Struct');
expect(struct.get('foo').toNumber()).toEqual(42);
expect(struct.get('bar').toString()).toEqual('testing');
});
});
});
+72
View File
@@ -0,0 +1,72 @@
// 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 { CodecTo } from '../types';
import U8a from './U8a';
const testDecode = (type: string, input: any, expected: string) =>
it(`can decode from ${type}`, () => {
const e = new U8a(input);
expect(e.toString()).toBe(expected);
});
const testEncode = (to: CodecTo, expected: any) =>
it(`can encode ${to}`, () => {
const e = new U8a([1, 2, 3, 4, 5]);
expect(e[to]()).toEqual(expected);
});
describe('U8a', () => {
let u8a: U8a;
beforeEach(() => {
u8a = new U8a([1, 2, 3, 4, 5]);
});
testDecode('Array', [1, 2, 3, 4, 5], '0x0102030405');
testDecode('hex', '0x0102030405', '0x0102030405');
testDecode('U8a', new Uint8Array([1, 2, 3, 4, 5]), '0x0102030405');
testDecode('Uint8Array', Uint8Array.from([1, 2, 3, 4, 5]), '0x0102030405');
testEncode('toJSON', '0x0102030405');
testEncode('toHex', '0x0102030405');
testEncode('toString', '0x0102030405');
testEncode('toU8a', Uint8Array.from([1, 2, 3, 4, 5]));
it('contains the length of the elements', () => {
expect(u8a.length).toEqual(5);
});
it('correctly encodes length', () => {
expect(u8a.encodedLength).toEqual(5);
});
it('allows wrapping of a pre-existing instance', () => {
expect(
new U8a(u8a).length
).toEqual(5);
});
it('implements subarray correctly', () => {
expect(u8a.subarray(1, 3)).toEqual(Uint8Array.from([2, 3]));
});
describe('utils', () => {
it('compares against other U8a', () => {
expect(u8a.eq(new Uint8Array([1, 2, 3, 4, 5]))).toBe(true);
});
it('compares against other U8a (non-length)', () => {
expect(u8a.eq(new Uint8Array([1, 2, 3, 4]))).toBe(false);
});
it('compares against other U8a (mismatch)', () => {
expect(u8a.eq(new Uint8Array([1, 2, 3, 4, 6]))).toBe(false);
});
it('compares against hex inputs', () => {
expect(u8a.eq('0x0102030405')).toBe(true);
});
});
});
+105
View File
@@ -0,0 +1,105 @@
// 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 { isU8a, isUndefined, u8aToHex, u8aToU8a } from '@plugnet/util';
import { AnyU8a, Codec } from '../types';
/**
* @name U8a
* @description
* A basic wrapper around Uint8Array, with no frills and no fuss. It does differ
* from other implementations wher it will consume the full Uint8Array as passed to
* it. As such it is meant to be subclassed where the wrapper takes care of the
* actual lengths instead of used directly.
* @noInheritDoc
*/
export default class U8a extends Uint8Array implements Codec {
constructor (value?: AnyU8a) {
super(
U8a.decodeU8a(value)
);
}
private static decodeU8a (value?: any): Uint8Array {
if (isU8a(value)) {
return value;
}
return u8aToU8a(value);
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return this.length;
}
/**
* @description Returns true if the type wraps an empty/default all-0 value
*/
get isEmpty (): boolean {
return !this.length || isUndefined(this.find((value) => !!value));
}
/**
* @description The length of the value
*/
get length (): number {
// only included here since we ignore inherited docs
return super.length;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
if (other instanceof Uint8Array) {
return (this.length === other.length) && isUndefined(
this.find((value, index) => value !== other[index])
);
}
return this.eq(U8a.decodeU8a(other));
}
/**
* @description Create a new subarray from the actual buffer. This is needed for compat reasons since a new Uint8Array gets returned here
* @param begin The position to start at
* @param end The position to end at
*/
subarray (begin: number, end?: number): Uint8Array {
return Uint8Array.from(this).subarray(begin, end);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this);
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.toHex();
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.toHex();
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return Uint8Array.from(this);
}
}
+33
View File
@@ -0,0 +1,33 @@
// 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 U8aFixed from './U8aFixed';
describe('U8aFixed', () => {
let u8a: U8aFixed;
beforeEach(() => {
u8a = new U8aFixed([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 32);
});
it('limits the length', () => {
expect(u8a.length).toEqual(4);
});
it('exposes the correct bitLength', () => {
expect(u8a.bitLength()).toEqual(32);
});
it('allows wrapping of a pre-existing instance', () => {
expect(
u8a.toU8a()
).toEqual(new Uint8Array([1, 2, 3, 4]));
});
it('allows empty values', () => {
expect(
new U8aFixed().toHex()
).toEqual('0x0000000000000000000000000000000000000000000000000000000000000000');
});
});
+55
View File
@@ -0,0 +1,55 @@
// 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 { isString, isU8a, u8aToU8a } from '@plugnet/util';
import { AnyU8a } from '../types';
import U8a from './U8a';
type BitLength = 8 | 16 | 32 | 64 | 128 | 160 | 256 | 512;
/**
* @name U8aFixed
* @description
* A U8a that manages a a sequence of bytes up to the specified bitLength. Not meant
* 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)
);
}
private static decodeU8aFixed (value: AnyU8a, bitLength: BitLength = 256): AnyU8a {
if (isU8a(value)) {
// ensure that we have an actual u8a with the full length as specified by
// the bitLength input (padded with zeros as required)
const byteLength = bitLength / 8;
const sub = value.subarray(0, byteLength);
if (sub.length === byteLength) {
return sub;
}
const u8a = new Uint8Array(byteLength);
u8a.set(sub, 0);
return u8a;
} else if (Array.isArray(value) || isString(value)) {
return U8aFixed.decodeU8aFixed(u8aToU8a(value), bitLength);
}
return value;
}
/**
* @description Returns the number of bits in the value
*/
bitLength () {
return this.length * 8;
}
}
+70
View File
@@ -0,0 +1,70 @@
// 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 BN from 'bn.js';
import UInt from './UInt';
describe('UInt', () => {
it('provides a toBn interface', () => {
expect(
new UInt(987).toBn().toNumber()
).toEqual(987);
});
it('provides a toNumber interface', () => {
expect(
new UInt(4567).toNumber()
).toEqual(4567);
});
it('converts to LE from the provided value', () => {
expect(
new UInt(1234567).toU8a()
).toEqual(new Uint8Array([135, 214, 18, 0, 0, 0, 0, 0]));
});
it('converts to LE from the provided value (bitLength)', () => {
expect(
new UInt(1234567, 32).toU8a()
).toEqual(new Uint8Array([135, 214, 18, 0]));
});
it('converts to hex/string', () => {
const u = new UInt('0x12', 16);
expect(u.toHex()).toEqual('0x0012');
expect(u.toString()).toEqual('18');
});
it('converts to equivalents', () => {
const a = new UInt('123');
expect(
new UInt(a).toNumber()
).toEqual(123);
});
it('converts to JSON representation based on flags/size', () => {
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');
});
describe('eq', () => {
const test = new UInt(12345);
it('compares against other BN values', () => {
expect(test.eq(new BN(12345))).toBe(true);
});
it('compares against other number values', () => {
expect(test.eq(12345)).toBe(true);
});
it('compares against hex values', () => {
expect(test.eq('0x3039')).toBe(true);
});
});
});
+55
View File
@@ -0,0 +1,55 @@
// 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 { AnyNumber } from '../types';
import { bnToHex, bnToU8a } from '@plugnet/util';
import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt';
/**
* @name UInt
* @description
* A generic unsigned integer codec. For Substrate all numbers are LE encoded,
* this handles the encoding and decoding of those numbers. Upon construction
* the bitLength is provided and any additional use keeps the number to this
* length. This extends `BN`, so all methods available on a normal `BN` object
* is available here.
* @noInheritDoc
*/
export default class UInt extends AbstractInt {
constructor (
value: AnyNumber = 0,
bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = false) {
super(
false,
value,
bitLength,
isHexJson
);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return bnToHex(this, {
bitLength: this._bitLength,
isLe: false,
isNegative: false
});
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
return bnToU8a(this, {
bitLength: this._bitLength,
isLe: true,
isNegative: false
});
}
}
+140
View File
@@ -0,0 +1,140 @@
// 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 extrinsics from '@plugnet/extrinsics/static';
import Method from '../primitive/Method';
import Text from '../primitive/Text';
import PropIndex from '../type/PropIndex';
import Proposal from '../type/Proposal';
import AccountId from '../type/AccountId';
import { Codec, CodecTo } from '../types';
import createType from './createType';
import Vector from './Vector';
import Tuple from './Tuple';
describe('Vector', () => {
let vector: Vector<Codec>;
beforeEach(() => {
vector = new Vector(Text, ['1', '23', '345', '4567', new Text('56789')]);
Method.injectMethods(extrinsics);
});
it('wraps a sequence of values', () => {
expect(vector.length).toEqual(5); // eslint-disable-line
});
it('has a sane representation for toString', () => {
expect(vector.toString()).toEqual('[1, 23, 345, 4567, 56789]');
});
it('encodes with length prefix', () => {
expect(vector.toU8a()).toEqual(new Uint8Array([
5 << 2,
1 << 2, 49,
2 << 2, 50, 51,
3 << 2, 51, 52, 53,
4 << 2, 52, 53, 54, 55,
5 << 2, 53, 54, 55, 56, 57
]));
});
it('allows contruction via JSON', () => {
expect(
new Vector(Text, ['6', '7']).toJSON()
).toEqual(['6', '7']);
});
it('exposes the type', () => {
expect(vector.Type).toEqual('Text');
});
it.skip('decodes a complex type via construction', () => {
const test = createType('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([
4, 10, 0, 0, 0, 0, 3, 80, 123, 10, 9, 34, 48, 120, 52, 50, 34, 58, 32, 34, 48, 120, 52, 51, 34, 10, 125, 10, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79
]));
const first = (test as Vector<Codec>)[0] as Tuple;
expect((first[0] as PropIndex).toNumber()).toEqual(10);
expect((first[1] as Proposal).callIndex).toEqual(new Uint8Array([0, 3]));
expect((first[2] as AccountId).toString()).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ');
});
describe('vector-like functions', () => {
it('allows retrieval of a specific item', () => {
expect(
vector[2].toString()
).toEqual('345');
});
it('exposes a working forEach', () => {
const result: { [index: number]: string } = {};
vector.forEach((e, i) => {
result[i] = e.toString();
});
expect(result).toEqual({
0: '1',
1: '23',
2: '345',
3: '4567',
4: '56789'
});
});
it('exposes a working filter', () => {
expect(
vector.filter((e, i) => i >= 3).toString()
).toEqual('4567,56789');
});
it('exposes a working map', () => {
expect(
vector.map((e) => e.toString().substr(0, 1))
).toEqual(['1', '2', '3', '4', '5']);
});
it('exposes a working reduce', () => {
expect(
vector.reduce((r, e) => `${r}${e}`, '')
).toEqual('123345456756789');
});
});
describe('encode', () => {
const testEncode = (to: CodecTo, expected: any) =>
it(`can encode ${to}`, () => {
expect(vector[to]()).toEqual(expected);
});
testEncode('toHex', '0x1404310832330c3334351034353637143536373839');
testEncode('toJSON', ['1', '23', '345', '4567', '56789']);
testEncode('toString', '[1, 23, 345, 4567, 56789]');
testEncode('toU8a', Uint8Array.from([20, 4, 49, 8, 50, 51, 12, 51, 52, 53, 16, 52, 53, 54, 55, 20, 53, 54, 55, 56, 57]));
});
describe('utils', () => {
const vec = new Vector(Text, ['123', '456']);
it('compares against codec types', () => {
expect(vec.eq([new Text('123'), new Text('456')])).toBe(true);
});
it('compares against codec + primitive types', () => {
expect(vec.eq(['123', new Text('456')])).toBe(true);
});
it('finds the index of an value', () => {
const myId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const vec = new Vector(AccountId, [
'5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw', '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
]);
expect(vec.indexOf(myId)).toEqual(2);
});
});
});
+185
View File
@@ -0,0 +1,185 @@
// 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 { u8aConcat, u8aToU8a, u8aToHex } from '@plugnet/util';
import Compact from './Compact';
import { Codec, Constructor } from '../types';
import { compareArray, decodeU8a } from './utils';
/**
* @name Vector
* @description
* This manages codec arrays. Internally it keeps track of the length (as decoded) and allows
* construction with the passed `Type` in the constructor. It is an extension to Array, providing
* specific encoding/decoding on top of the base type.
* @noInheritDoc
*/
export default class Vector<
T extends Codec
> extends Array<T> implements Codec {
private _Type: Constructor<T>;
constructor (Type: Constructor<T>, value: Vector<any> | Uint8Array | string | Array<any> = [] as Array<any>) {
super(
...Vector.decodeVector(Type, value)
);
this._Type = Type;
}
static decodeVector<T extends Codec> (Type: Constructor<T>, value: Vector<any> | Uint8Array | string | Array<any>): Array<T> {
if (Array.isArray(value)) {
return value.map((entry) =>
entry instanceof Type
? entry
: new Type(entry)
);
}
const u8a = u8aToU8a(value);
const [offset, _length] = Compact.decodeU8a(u8a);
const length = _length.toNumber();
return decodeU8a(u8a.subarray(offset), new Array(length).fill(Type)) as T[];
}
static with<O extends Codec> (Type: Constructor<O>): Constructor<Vector<O>> {
return class extends Vector<O> {
constructor (value?: Array<any>) {
super(Type, value);
}
};
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.length === 0;
}
/**
* @description The type for the items
*/
get Type (): string {
return this._Type.name;
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
return this.reduce((total, raw) => {
return total + raw.encodedLength;
}, Compact.encodeU8a(this.length).length);
}
/**
* @description The length of the value
*/
get length (): number {
// only included here since we ignore inherited docs
return super.length;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
return compareArray(this, other);
}
/**
* @description Finds the index of the value in the array
*/
indexOf (_other?: any): number {
// convert type first, this removes overhead from the eq
const other = _other instanceof this._Type
? _other
: new this._Type(_other);
for (let i = 0; i < this.length; i++) {
if (other.eq(this[i])) {
return i;
}
}
return -1;
}
/**
* @description Converts the Object to an standard JavaScript Array
*/
toArray (): Array<T> {
return Array.from(this);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): any {
return this.map((entry) =>
entry.toJSON()
);
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
// Overwrite the default toString representation of Array.
const data = this.map((entry) =>
entry.toString()
);
return `[${data.join(', ')}]`;
}
/**
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
const encoded = this.map((entry) =>
entry.toU8a(isBare)
);
return isBare
? u8aConcat(...encoded)
: u8aConcat(
Compact.encodeU8a(this.length),
...encoded
);
}
// Below are methods that we override. When we do a `new Vector(...).map()`,
// we want it to return an Array. We only override the methods that return a
// new instance.
/**
* @description Filters the array with the callback
* @param callbackfn The filter function
* @param thisArg The `this` object to apply the result to
*/
filter (callbackfn: (value: T, index: number, array: Array<T>) => any, thisArg?: any): Array<T> {
return this.toArray().filter(callbackfn, thisArg);
}
/**
* @description Maps the array with the callback
* @param callbackfn The mapping function
* @param thisArg The `this` onject to apply the result to
*/
map<U> (callbackfn: (value: T, index: number, array: Array<T>) => U, thisArg?: any): Array<U> {
return this.toArray().map(callbackfn, thisArg);
}
}
+227
View File
@@ -0,0 +1,227 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import createType, { TypeDefInfo, getTypeClass, getTypeDef, typeSplit } from './createType';
describe('typeSplit', () => {
it('splits simple types into an array', () => {
expect(
typeSplit('Text, u32, u64')
).toEqual(['Text', 'u32', 'u64']);
});
it('splits nested combinations', () => {
expect(
typeSplit('Text, (u32), Vec<u64>')
).toEqual(['Text', '(u32)', 'Vec<u64>']);
});
it('keeps nested tuples together', () => {
expect(
typeSplit('Text, (u32, u128), Vec<u64>')
).toEqual(['Text', '(u32, u128)', 'Vec<u64>']);
});
it('keeps nested vector tuples together', () => {
expect(
typeSplit('Text, (u32, u128), Vec<(u64, u32)>')
).toEqual(['Text', '(u32, u128)', 'Vec<(u64, u32)>']);
});
it('allows for deep nesting', () => {
expect(
typeSplit('Text, (u32, (u128, u8)), Vec<(u64, (u32, u32))>')
).toEqual(['Text', '(u32, (u128, u8))', 'Vec<(u64, (u32, u32))>']);
});
it('checks for unclosed vec', () => {
expect(
() => typeSplit('Text, Vec<u64')
).toThrow(/Invalid defintion/);
});
it('checks for unclosed tuple', () => {
expect(
() => typeSplit('Text, (u64, u32')
).toThrow(/Invalid defintion/);
});
});
describe('getTypeValue', () => {
it('does not allow invalid tuples, end )', () => {
expect(
() => getTypeDef('(u64, u32')
).toThrow(/Expected '\(' closing with '\)'/);
});
it('does not allow invalid vectors, end >', () => {
expect(
() => getTypeDef('Vec<u64')
).toThrow(/Expected 'Vec<' closing with '>'/);
});
it('returns a type structure', () => {
expect(
getTypeDef('(u32, Compact<u32>, Vec<u64>, Option<u128>, (Text, Vec<(Bool, u128)>))')
).toEqual({
info: TypeDefInfo.Tuple,
type: '(u32, Compact<u32>, Vec<u64>, Option<u128>, (Text, Vec<(Bool, u128)>))',
sub: [
{
info: TypeDefInfo.Plain,
type: 'u32'
},
{
info: TypeDefInfo.Compact,
type: 'Compact<u32>',
sub: {
info: TypeDefInfo.Plain,
type: 'u32'
}
},
{
info: TypeDefInfo.Vector,
type: 'Vec<u64>',
sub: {
info: TypeDefInfo.Plain,
type: 'u64'
}
},
{
info: TypeDefInfo.Option,
type: 'Option<u128>',
sub: {
info: TypeDefInfo.Plain,
type: 'u128'
}
},
{
info: TypeDefInfo.Tuple,
type: '(Text, Vec<(Bool, u128)>)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'Text'
},
{
info: TypeDefInfo.Vector,
type: 'Vec<(Bool, u128)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(Bool, u128)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'Bool'
},
{
info: TypeDefInfo.Plain,
type: 'u128'
}
]
}
}
]
}
]
});
});
it('returns a type structure (actual)', () => {
expect(
getTypeDef('Vec<(PropIndex, Proposal, AccountId)>')
).toEqual({
info: TypeDefInfo.Vector,
type: 'Vec<(PropIndex, Proposal, AccountId)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(PropIndex, Proposal, AccountId)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'PropIndex'
},
{
info: TypeDefInfo.Plain,
type: 'Proposal'
},
{
info: TypeDefInfo.Plain,
type: 'AccountId'
}
]
}
});
});
it('returns an actual Struct', () => {
expect(
getTypeDef('{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}')
).toEqual({
info: TypeDefInfo.Struct,
type: '{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}',
sub: [
{
info: TypeDefInfo.Plain,
name: 'balance',
type: 'Balance'
},
{
info: TypeDefInfo.Plain,
name: 'account_id',
type: 'AccountId'
},
{
info: TypeDefInfo.Tuple,
name: 'log',
type: '(u64, Signature)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'u64'
},
{
info: TypeDefInfo.Plain,
type: 'Signature'
}
]
}
]
});
});
});
describe('getTypeClass', () => {
it('does not allow invalid types', () => {
expect(
() => getTypeClass('SomethingInvalid' as any)
).toThrow(/determine type/);
});
});
describe('createType', () => {
it('allows creation of a Struct', () => {
expect(
createType('{"balance":"Balance","index":"u32"}', {
balance: 1234,
index: '0x10'
}).toJSON()
).toEqual({
balance: 1234,
index: 16
});
});
it('allows creation of a Enum (simple)', () => {
expect(
createType('{"_enum": ["A", "B", "C"]}', 1).toJSON()
).toEqual({ B: null });
});
it('allows creation of a Enum (parametrised)', () => {
expect(
createType('{"_enum": {"A": null, "B": "u32", "C": null} }', 1).toJSON()
).toEqual({ B: 0 });
});
});
+218
View File
@@ -0,0 +1,218 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { assert } from '@plugnet/util';
import { Codec, Constructor } from '../types';
import Text from '../primitive/Text';
import EnumType from './EnumType';
import Compact from './Compact';
import Option from './Option';
import Struct from './Struct';
import Tuple from './Tuple';
import UInt from './UInt';
import Vector from './Vector';
import getRegistry from './typeRegistry';
import { Linkage } from './Linkage';
export enum TypeDefInfo {
Compact,
Enum,
Option,
Plain,
Struct,
Tuple,
Vector,
Linkage
}
export type TypeDef = {
info: TypeDefInfo,
name?: string,
type: string,
sub?: TypeDef | Array<TypeDef>
};
// safely split a string on ', ' while taking care of any nested occurences
export function typeSplit (type: string): Array<string> {
let sDepth = 0;
let tDepth = 0;
let vDepth = 0;
let start = 0;
const result = [];
for (let index = 0; index < type.length; index++) {
switch (type[index]) {
case ',':
// we are not nested, add the type
if (sDepth === 0 && tDepth === 0 && vDepth === 0) {
result.push(type.substr(start, index - start).trim());
start = index + 1;
}
break;
// inc struct depth, start found
case '{': sDepth++; break;
// dec struct depth, end found
case '}': sDepth--; break;
// inc tuple depth, start found
case '(': tDepth++; break;
// dec tuple depth, end found
case ')': tDepth--; break;
// inc compact/vec depth, start found
case '<': vDepth++; break;
// dec compact/vec depth, end found
case '>': vDepth--; break;
// normal character
default: break;
}
}
assert(!sDepth && !tDepth && !vDepth, `Invalid defintion (missing terminators) found in ${type}`);
// the final leg of the journey
result.push(type.substr(start, type.length - start).trim());
return result;
}
export function getTypeDef (_type: Text | string, name?: string): TypeDef {
const type = _type.toString().trim();
const value: TypeDef = {
info: TypeDefInfo.Plain,
name,
type
};
let subType = '';
const startingWith = (type: string, start: string, end: string): boolean => {
if (type.substr(0, start.length) !== start) {
return false;
}
assert(type[type.length - 1] === end, `Expected '${start}' closing with '${end}'`);
subType = type.substr(start.length, type.length - start.length - 1);
return true;
};
if (startingWith(type, '(', ')')) {
value.info = TypeDefInfo.Tuple;
value.sub = typeSplit(subType).map((inner) => getTypeDef(inner));
} else if (startingWith(type, '{', '}')) {
const parsed = JSON.parse(type);
const keys = Object.keys(parsed);
if (keys.length === 1 && keys[0] === '_enum') {
const details = parsed[keys[0]];
// not as pretty, but remain compatible with oo7 for both struct and Array types
value.sub = Array.isArray(details)
? details.map((name) => ({ info: TypeDefInfo.Plain, name, type: 'Null' }))
: Object.keys(details).map((name) => ({ info: TypeDefInfo.Plain, name, type: details[name] || 'Null' }));
value.info = TypeDefInfo.Enum;
} else {
value.info = TypeDefInfo.Struct;
value.sub = keys.map((name) => getTypeDef(parsed[name], name));
}
} else if (startingWith(type, 'Compact<', '>')) {
value.info = TypeDefInfo.Compact;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'Option<', '>')) {
value.info = TypeDefInfo.Option;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'Vec<', '>')) {
value.info = TypeDefInfo.Vector;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'Linkage<', '>')) {
value.info = TypeDefInfo.Linkage;
value.sub = getTypeDef(subType);
}
return value;
}
// Returns the type Class for construction
export function getTypeClass (value: TypeDef): Constructor {
const Type = getRegistry().get(value.type);
if (Type) {
return Type;
}
switch (value.info) {
case TypeDefInfo.Compact:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Compact');
return Compact.with(
getTypeClass(value.sub as TypeDef) as Constructor<UInt>
);
case TypeDefInfo.Enum:
assert(value.sub && Array.isArray(value.sub), 'Expected subtype for Enum');
return EnumType.with(
(value.sub as Array<TypeDef>).reduce((result, sub, index) => {
result[sub.name as string] = getTypeClass(sub);
return result;
}, {} as { [index: string]: Constructor })
);
case TypeDefInfo.Option:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Option');
return Option.with(
getTypeClass(value.sub as TypeDef)
);
case TypeDefInfo.Struct:
assert(Array.isArray(value.sub), 'Expected nested subtypes for Struct');
return Struct.with(
(value.sub as Array<TypeDef>).reduce((result, sub) => {
result[sub.name as string] = getTypeClass(sub);
return result;
}, {} as { [index: string]: Constructor })
);
case TypeDefInfo.Tuple:
assert(Array.isArray(value.sub), 'Expected nested subtypes for Tuple');
return Tuple.with(
(value.sub as Array<TypeDef>).map(getTypeClass)
);
case TypeDefInfo.Vector:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Vector');
return Vector.with(
getTypeClass(value.sub as TypeDef)
);
case TypeDefInfo.Linkage:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Linkage');
return Linkage.withKey(
getTypeClass(value.sub as TypeDef)
);
}
throw new Error(`Unable to determine type from '${value.type}'`);
}
export function createClass (type: Text | string): Constructor {
return getTypeClass(
getTypeDef(type)
);
}
export default function createType (type: Text | string, value?: any): Codec {
// l.debug(() => ['createType', { type, value }]);
const Type = createClass(type);
return new Type(value);
}
+26
View File
@@ -0,0 +1,26 @@
// 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.
// NOTE We are not exporting everything here. These _should_ be enough to use the
// actual interfaces from a "create-a-working-coder" perspective. If not, we should
// expand with slight care (for instance, Length is really only used internally to
// others, so there _should_ not be need for direct use)
// These are the base codec types, generally used for construction
export { default as Compact } from './Compact';
export { default as Enum } from './Enum';
export { default as EnumType } from './EnumType';
export { default as Option } from './Option';
export { default as Set } from './Set';
export { default as Struct } from './Struct';
export { default as Tuple } from './Tuple';
export { default as Vector } from './Vector';
// Convenience base classes, used as "anything of this type" bases
export { default as U8a } from './U8a';
export { default as UInt } from './UInt';
// Type management helper functions
export { default as createType, createClass, getTypeClass, getTypeDef, TypeDef, TypeDefInfo } from './createType';
export { default as getTypeRegistry } from './typeRegistry';
+73
View File
@@ -0,0 +1,73 @@
// 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 { isFunction, isString, isUndefined } from '@plugnet/util';
import { Constructor, RegistryTypes } from '../types';
import { createClass } from './createType';
export class TypeRegistry {
static readonly defaultRegistry: TypeRegistry = new TypeRegistry();
private _registry: Map<string, Constructor> = new Map();
register (type: Constructor | RegistryTypes): void;
register (name: string, type: Constructor): void;
register (arg1: string | Constructor | RegistryTypes, arg2?: Constructor): void {
if (isString(arg1)) {
const name = arg1;
const type = arg2!;
this._registry.set(name, type);
} else if (isFunction(arg1)) {
const name = arg1.name;
const type = arg1;
this._registry.set(name, type);
} else {
this.registerObject(arg1);
}
}
private registerObject (obj: RegistryTypes, overwrite: boolean = true) {
Object.entries(obj).forEach(([name, type]) => {
if (overwrite || !this.get(name)) {
if (isString(type)) {
this._registry.set(name, createClass(type));
} else if (isFunction(type)) {
// This _looks_ a bit funny, but `typeof Clazz === 'function'
this._registry.set(name, type);
} else {
this._registry.set(name, createClass(JSON.stringify(type)));
}
}
});
}
get (name: string): Constructor | undefined {
return this._registry.get(name);
}
getOrThrow (name: string, msg?: string): Constructor {
const type = this.get(name);
if (isUndefined(type)) {
throw new Error(msg || `type ${name} not found`);
}
return type;
}
}
let defaultRegistry: TypeRegistry;
export default function getDefaultRegistry () {
if (!defaultRegistry) {
const defaultTypes = require('../index.types');
defaultRegistry = new TypeRegistry();
defaultRegistry.register({ ...defaultTypes });
}
return defaultRegistry;
}
@@ -0,0 +1,48 @@
// 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 U32 from '../../primitive/U32';
import compareArray from './compareArray';
describe('compareArray', () => {
const a = [new U32(123), new U32(456), new U32(789)];
it('returns false when second param is a non-array', () => {
expect(
compareArray(a, 123)
).toBe(false);
});
it('compares array of codec agains primitive', () => {
expect(
compareArray(a, [123, 456, 789])
).toBe(true);
});
it('compares array of codec agains codec', () => {
expect(
compareArray(a, [new U32(123), new U32(456), new U32(789)])
).toBe(true);
});
it('compares primitive against primitive', () => {
expect(
compareArray(
[123, 456], [123, 456]
)
).toBe(true);
});
it('returns false when lengths are not matching', () => {
expect(
compareArray(a, [123])
).toBe(false);
});
it('returns false when value mismatches', () => {
expect(
compareArray(a, [123, 456, 999])
).toBe(false);
});
});
@@ -0,0 +1,21 @@
// 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 { isFunction, isUndefined } from '@plugnet/util';
// NOTE These are used internally and when comparing objects, expects that
// when the second is an Array<Codec> that the first has to be as well
export default function compareArray (a: Array<any>, b?: any): boolean {
if (Array.isArray(b)) {
return (a.length === b.length) && isUndefined(
a.find((value, index) =>
isFunction(value.eq)
? !value.eq(b[index])
: value !== b[index]
)
);
}
return false;
}
@@ -0,0 +1,81 @@
// 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 } from '../../types';
import U32 from '../../primitive/U32';
import compareMap from './compareMap';
describe('compareMap', () => {
const a = new Map<string, Codec>([
['decimals', new U32(15)],
['missing', new U32(10)],
['foobar', new U32(5)]
]);
const b = new Map<string, any>([
['decimals', 15],
['missing', 10],
['foobar', 5]
]);
const c = new Map<string, Codec>([
['decimals', new U32(15)],
['missing', new U32(10)],
['foobar', new U32(5)]
]);
it('compares Map<string, Codec> against Object', () => {
expect(
compareMap(a, {
decimals: 15,
foobar: 5,
missing: 10
})
).toBe(true);
});
it('compares Map<string, any> against entries array', () => {
expect(
compareMap(b, [
['missing', 10], ['decimals', 15], ['foobar', 5]
])
).toBe(true);
});
it('compares between 2 maps', () => {
expect(compareMap(a, b)).toBe(true);
});
it('compares between 2 maps (both codec)', () => {
expect(compareMap(a, c)).toBe(true);
});
it('returns false when second param is a non-map, non-array, non-object', () => {
expect(
compareMap(a, 123)
).toBe(false);
});
it('returns false when second param is a array with non-entries', () => {
expect(
compareMap(a, [123, 456, 789])
).toBe(false);
});
it('returns false when second param is a array with non-entries (only key)', () => {
expect(
compareMap(a, [[123], [456], [789]])
).toBe(false);
});
it('returns false when properties are missing', () => {
expect(
compareMap(a, [['decimals', 15], ['wrong', 10], ['foobar', 5]])
).toBe(false);
});
it('returns false when lengths do not match', () => {
expect(
compareMap(a, [['decimals', 15]])
).toBe(false);
});
});
@@ -0,0 +1,34 @@
// 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 { isFunction, isObject, isUndefined } from '@plugnet/util';
// NOTE These are used internally and when comparing objects, expects that
// when the second is an Map<string, Codec> that the first has to be as well
export default function compareMap (a: Map<any, any>, b?: any): boolean {
if (Array.isArray(b)) {
return (a.size === b.length) && isUndefined(
b.find((entry) => {
if (!Array.isArray(entry) || entry.length !== 2) {
return true;
}
const value = a.get(entry[0]);
return isUndefined(value) || (
// Codec has .eq, use it here
isFunction(value.eq)
? !value.eq(entry[1])
: value !== entry[1]
);
})
);
} else if (b instanceof Map) {
return compareMap(a, [...b.entries()]);
} else if (isObject(b)) {
return compareMap(a, Object.entries(b));
}
return false;
}
@@ -0,0 +1,27 @@
// 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 } from '../../types';
/**
* Given an u8a, and an array of Type constructors, decode the u8a against the
* types, and return an array of decoded values.
*
* @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[] {
const types = Array.isArray(_types)
? _types
: Object.values(_types);
if (!types.length) {
return [];
}
const Type = types[0];
const value = new Type(u8a);
return [value].concat(decodeU8a(u8a.subarray(value.encodedLength), types.slice(1)));
}
+7
View File
@@ -0,0 +1,7 @@
// 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.
export { default as compareArray } from './compareArray';
export { default as compareMap } from './compareMap';
export { default as decodeU8a } from './decodeU8a';
+45
View File
@@ -0,0 +1,45 @@
// 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 { Constructor } from './types';
import extrinsics from '@plugnet/extrinsics/static';
import * as Classes from './index.types';
const Types = Classes as { [index: string]: Constructor };
describe('types', () => {
describe('default creation', () => {
Object.keys(Types).forEach((name) => {
it(`creates an empty ${name}`, () => {
const constructFn = () =>
new Types[name]();
if (name === 'Origin') {
expect(constructFn).toThrow();
} else {
expect(constructFn).not.toThrow();
}
});
});
});
describe('default creation (empty Bytes)', () => {
(Types.Method as any).injectMethods(extrinsics);
Object.keys(Types).forEach((name) => {
it(`creates an empty ${name} (from empty bytes)`, () => {
const constructFn = () =>
new Types[name](new Types.Bytes());
if (name === 'Origin') {
expect(constructFn).toThrow();
} else {
expect(constructFn).not.toThrow();
}
});
});
});
});
+14
View File
@@ -0,0 +1,14 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
// NOTE When adding any types here, we need to update the coumentation links as
// well - <root>/docs/SUMMARY.md as well as ../README.md
import { assertSingletonPackage } from '@plugnet/util';
assertSingletonPackage('@polkadot/types');
export { default as ContractAbi } from './ContractAbi';
export * from './codec';
export * from './index.types';
+14
View File
@@ -0,0 +1,14 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
// NOTE When adding any types here, we need to update the coumentation links as
// well - <root>/docs/SUMMARY.md as well as ../README.md
/**
* @summary Type definitions that are used in the system
*/
export { default as Metadata } from './Metadata';
export * from './primitive';
export * from './rpc';
export * from './type';
@@ -0,0 +1,16 @@
{
"jsonrpc": "2.0",
"method": "state_storage",
"params": {
"result": {
"block": "0x3265584ff8e6a66cb6c334cdaf8bb6938b49b96e644b5bacc0d06f2b13f75230",
"changes": [
[
"0x329586a7b97f2ac15f6c26a02c3c5621",
"0x2082c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf54de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e758d67e0f9be62dce75adbb005e8097de5c45f32b1ba7264717df2db4ae9f276e8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081ca070532405ebf20fba389cbabfe1885cc134ee18028d488714eae621b47baf9d227cee94fa4e88d8d25abe706f15aca03b1d753d433f5ef9aa9ad1bcf5e5b81e040c8dc048a6d129803caa851c4c9633610068e4ef9eaa0bfbf40dfbfd43d9222347812ef77d9fd3cca1673e1b2bde54da96fddcf79d92832b1e2a819724f140"
]
]
},
"subscription": 43614
}
}
@@ -0,0 +1,16 @@
{
"jsonrpc": "2.0",
"method": "state_storage",
"params": {
"result": {
"block": "0x4c51fb36c859f9d2554ef0dadf53351c343f2746221a963d630bfe396728842d",
"changes": [
[
"0xcc956bdb7605e3547539f321ac2bc95c",
"0x0400000000000000"
]
]
},
"subscription": 14
}
}
@@ -0,0 +1,16 @@
{
"jsonrpc": "2.0",
"method": "state_storage",
"params": {
"result": {
"block": "0xa7f1ac9f18dded8e5825205d3e5260cf53ba01f629d9ebc1b80601b45ed8c358",
"changes": [
[
"0xcc956bdb7605e3547539f321ac2bc95c",
"0x10000000000000000001000000010037e027d776cd005c12bcf6722421374a9037167a0ceaf918f341c4ad68d54e59070000000000010000000102d2de7394ae047a5502ad9adb9cc69ff6fe484033bfce874d775da947487cd83237e027d776cd005c12bcf6722421374a9037167a0ceaf918f341c4ad68d54e59e80300000000000000000000000000000000000000000000000000000000000000010000000000"
]
]
},
"subscription": 19
}
}
@@ -0,0 +1,16 @@
{
"jsonrpc": "2.0",
"method": "state_storage",
"params": {
"result": {
"block": "0xa7f1ac9f18dded8e5825205d3e5260cf53ba01f629d9ebc1b80601b45ed8c358",
"changes": [
[
"0xcc956bdb7605e3547539f321ac2bc95c",
"0x140000000000000000010000000203306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc204a83f1c09be797bc3d9adce29818368b276a84e6b545ced492c25c948978d7f83930000000000000000000000000000000000000000000000000000000000000000100000000000103006400000000000000010d000100000001000000000000000000000000000000"
]
]
},
"subscription": 19
}
}
@@ -0,0 +1,10 @@
{
"jsonrpc": "2.0",
"method": "author_extrinsicUpdate",
"params": {
"result": {
"finalised": "0xc465b92a72b1d20918d64cd4effa70c2bb58b53a3f8c24c3ac8fd8f465f059b4"
},
"subscription": 9
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"jsonrpc": "2.0",
"result": {
"digest": {
"logs": []
},
"extrinsicsRoot": "0x45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0",
"number": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"stateRoot": "0x294c3470ae3be7555240b9d034ec19c3715ba2c3f20b92441f8cea0cab66ab56"
},
"id": 1
}
+20
View File
@@ -0,0 +1,20 @@
{
"jsonrpc": "2.0",
"result": {
"digest": {
"logs": [
{
"Seal": [
256804359,
"0x2c0e0ee8a5d1a073da3f8db002b8eaebcd7b1a7eb25662e09f9ef0ac58d96bf17896256cd5f0bc672f96aab49fe5163ca5cb4c1a0f047dc39e89cd4a9eb2ea05"
]
}
]
},
"extrinsicsRoot": "0x1da0d30001d07991baef3c5c65234c2e88bec659dc0d7138a7e7267e2bacbeb7",
"number": "0xb66",
"parentHash": "0xb58e4d84c2e0db1cdc8c8830aa719015f8c855f29a0dff2b158a160f29f73ba0",
"stateRoot": "0x22f6f5b6992ab87ea5f52b2732ed9e22a88643ed3a97251986857633687fbc54"
},
"id": 1
}
@@ -0,0 +1,39 @@
{
"jsonrpc": "2.0",
"result": {
"apis": [
[
[
105,
110,
104,
101,
114,
101,
110,
116
],
1
],
[
[
118,
97,
108,
105,
100,
97,
116,
120
],
1
]
],
"authoring_version": 1,
"impl_name": "substrate-node",
"impl_version": 0,
"spec_name": "node",
"spec_version": 1
},
"id": 1
}
@@ -0,0 +1,43 @@
{
"jsonrpc": "2.0",
"result": {
"block": {
"extrinsics": [
"0x0102004629c35b00000000",
"0x8110f8e1ebdd3cdef7423d24fe68f3863945ea21c190907d7f3394ddf153f633c77b894af36f7a36e7ec767f4593bfd8d084b66d805605905054327a08ff604187080100000000000000270e0100ff0e6422725af0e9aede3bef6eba77bc87afadf60b9735057ae93801f4c472ad7b00407a10f35a00000000000000000000"
],
"header": {
"digest": {
"logs": []
},
"extrinsicsRoot": "0x79e749f5b04d6fde65503874daf1ca2a95509dfada574565fb81c11af236397c",
"number": 206053,
"parentHash": "0x6799fcd718579b07900bde8e14e2a1bde1c386f4539ef9cbb4407eebf5cb4857",
"stateRoot": "0xbfe02f7c26e5dad1226eccb1f4ef5306312602f7e1edbb9be50e8b87d9c7840b"
}
},
"justification": {
"hash": "0xfab4bb3a8b0a072d3d09858dc865ad2750b2e708536b6dacc89fad369eba781a",
"round_number": 0,
"signatures": [
[
"0x8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c",
"0x84e343ddfd126d7c7a5c8feeac33dae947a08e4bee7b94080448fd31ae00f3a5e1a3e916926535976671ee61c430c215dbda1a781a7692a6ffc8f955b9d2d603"
],
[
"0x82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5",
"0x67089e8cbf4fec1c362de972c1430c468f572ceba0a26ca1ed4782155efc3ae2156cef02fb57f0012fe7766d932d2ccde9fd8d48bb4c869d7461079b66c1f002"
],
[
"0x063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5",
"0x9822441c8196fe9737e8d1aa49f4c0da046c15eba695fa89f98e2736fcc747e5ae88720fb554cc509d1e405e04194fda10a956eb85c76bde057ba01ffeef8507"
],
[
"0x4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7",
"0x1753981ad2dd67d18ca7026b2b2e8c3e0071da561eda8e3532a5b4a713415248f1df6ef32b32136ba2cb75cd4b4165aa9e1abe259b533046fb199e41b67f9406"
]
]
}
},
"id": 8
}
@@ -0,0 +1,58 @@
{
"jsonrpc": "2.0",
"result": {
"block": {
"extrinsics": [
"0x010200b0b1c85b00000000"
],
"header": {
"digest": {
"logs": [
{
"AuthoritiesChange": [
"0x8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c",
"0x4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7",
"0x063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5",
"0x82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5",
"0x040c8dc048a6d129803caa851c4c9633610068e4ef9eaa0bfbf40dfbfd43d922",
"0x2347812ef77d9fd3cca1673e1b2bde54da96fddcf79d92832b1e2a819724f140",
"0x673bda1eda2aa719d707473163d45300819f2da9fe6fdad6d74ce222ada2665a"
]
}
]
},
"extrinsicsRoot": "0x295fe0f63daaa9552f497f6c4afcbab59ca5c681da9219907f188c765b73fb92",
"number": 262560,
"parentHash": "0x99b5b6d28a18c1fee26aa18a7f15b5ab20d416bc70319b4c3b3a7ae5a98284ea",
"stateRoot": "0x24bab7fe0fee187a57bd0029ba151e81a0bae0fea96ad9f85c579c1ba9d88948"
}
},
"justification": {
"hash": "0x44bc10702350e3548eb8ea52354e8e07a5646e108f0aceef9488131f6f929f3b",
"round_number": 0,
"signatures": [
[
"0x063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5",
"0xe551027d14a2107328685f14a6e6b6a217d0e587ed40b55e3a356f4d02468fb968d9d1dd9d0c7026b712bf4d2eac3bc3ff9eeb2c9560a40575f41df69e769804"
],
[
"0x8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c",
"0xf303671e97a9b1811d3c0e1b87ebeb89f340ccc5d0d634c9596ca4764943561304df268498c133e88ab3a859d86821d85ad48ac53c54df9ec85e2a515adb0b00"
],
[
"0x82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5",
"0xb2e326eb1ea3a803e6f331cbf2da8add553f08450a14fe1a26ba31039e3e1a70c6dcffd3361b94b973cdc7422f9e9b99edaea98117dda35275e6a36e6c54e30d"
],
[
"0x4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7",
"0x1f739aa8cfcaa81362e3eb9c574844df40e3db009676d6065a7b5662d5e00b1385526257ba43a43cf84e7873354e3919e0dc2ac85a0426b43b3f37d311f64907"
],
[
"0x2347812ef77d9fd3cca1673e1b2bde54da96fddcf79d92832b1e2a819724f140",
"0x01370b87621008e9ee55476dc0c4246862aed787b94c27105eaba9c09e197882c6f40436ce2f45f0dbae03ff81af117bdaabc18f627b72aa6318326f6a067b05"
]
]
}
},
"id": 8
}

Some files were not shown because too many files have changed in this diff Show More