init
This commit is contained in:
@@ -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.
|
||||
@@ -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]]
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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));
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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]));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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"}');
|
||||
});
|
||||
});
|
||||
@@ -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)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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)));
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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';
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"digest": {
|
||||
"logs": []
|
||||
},
|
||||
"extrinsicsRoot": "0x45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x294c3470ae3be7555240b9d034ec19c3715ba2c3f20b92441f8cea0cab66ab56"
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user