Metadata v2 (#792)
* metadata v2 * Try to keep additional data form later metadata version
This commit is contained in:
committed by
Jaco Greeff
parent
4e88b831ff
commit
143cd2f9bb
@@ -11,14 +11,17 @@ import Struct from '../codec/Struct';
|
||||
import Null from '../primitive/Null';
|
||||
import MetadataV0 from './v0';
|
||||
import MetadataV1 from './v1';
|
||||
import MetadataV2 from './v2';
|
||||
import v1ToV0 from './v1/toV0';
|
||||
import v2ToV1 from './v2/toV1';
|
||||
import MagicNumber from './MagicNumber';
|
||||
|
||||
class MetadataEnum extends EnumType<Null | MetadataV1> {
|
||||
class MetadataEnum extends EnumType<Null | MetadataV1 | MetadataV2> {
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
MetadataV0, // once rolled-out, can replace this with Null
|
||||
MetadataV1
|
||||
MetadataV1, // once rolled-out, can replace this with Null
|
||||
MetadataV2
|
||||
}, value);
|
||||
}
|
||||
|
||||
@@ -36,6 +39,13 @@ class MetadataEnum extends EnumType<Null | MetadataV1> {
|
||||
return this.value as MetadataV1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the wrapped values as a V1 object
|
||||
*/
|
||||
get asV2 (): MetadataV2 {
|
||||
return this.value as MetadataV2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The version this metadata represents
|
||||
*/
|
||||
@@ -51,6 +61,7 @@ class MetadataEnum extends EnumType<Null | MetadataV1> {
|
||||
*/
|
||||
export default class MetadataVersioned extends Struct implements MetadataInterface {
|
||||
private _convertedV0?: MetadataV0;
|
||||
private _convertedV1?: MetadataV1;
|
||||
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
@@ -88,10 +99,10 @@ export default class MetadataVersioned extends Struct implements MetadataInterfa
|
||||
return this.metadata.asV0;
|
||||
}
|
||||
|
||||
assert(this.metadata.version === 1, `Cannot convert metadata from v${this.metadata.version} to v0`);
|
||||
let v1 = this.asV1;
|
||||
|
||||
if (isUndefined(this._convertedV0)) {
|
||||
this._convertedV0 = v1ToV0(this.metadata.asV1);
|
||||
this._convertedV0 = v1ToV0(v1);
|
||||
}
|
||||
|
||||
return this._convertedV0;
|
||||
@@ -101,9 +112,26 @@ export default class MetadataVersioned extends Struct implements MetadataInterfa
|
||||
* @description Returns the wrapped values as a V1 object
|
||||
*/
|
||||
get asV1 (): MetadataV1 {
|
||||
assert(this.metadata.version === 1, `Cannot convert metadata from v${this.metadata.version} to v1`);
|
||||
if (this.metadata.version === 1) {
|
||||
return this.metadata.asV1;
|
||||
}
|
||||
|
||||
return this.metadata.asV1;
|
||||
assert(this.metadata.version === 2, `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 V1 object
|
||||
*/
|
||||
get asV2 (): MetadataV2 {
|
||||
assert(this.metadata.version === 2, `Cannot convert metadata from v${this.metadata.version} to v2`);
|
||||
|
||||
return this.metadata.asV2;
|
||||
}
|
||||
|
||||
getUniqTypes (throwError: boolean): Array<string> {
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import metadata from './v1/static';
|
||||
import metadata from './v2/static';
|
||||
|
||||
export default metadata;
|
||||
|
||||
@@ -16,8 +16,8 @@ describe('Metadata', () => {
|
||||
const metadata = new Metadata(rpcData);
|
||||
const str = JSON.stringify(metadata.toJSON());
|
||||
|
||||
console.error(str);
|
||||
console.error(metadata.getUniqTypes(true));
|
||||
// console.error(str);
|
||||
// console.error(metadata.getUniqTypes(true));
|
||||
|
||||
expect(metadata.events.length).not.toBe(0);
|
||||
expect(str).toEqual(JSON.stringify(latestParsed));
|
||||
|
||||
@@ -136,11 +136,17 @@ export class StorageFunctionModifier extends Enum {
|
||||
}
|
||||
|
||||
export class MapType extends Struct {
|
||||
private _isLinked = false;
|
||||
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
key: Type,
|
||||
value: Type
|
||||
}, value);
|
||||
|
||||
if (value && value.isLinked) {
|
||||
this._isLinked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,6 +162,13 @@ export class MapType extends Struct {
|
||||
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 {
|
||||
|
||||
+7
-7
@@ -4,21 +4,21 @@
|
||||
|
||||
import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
|
||||
|
||||
import createType from '../codec/createType';
|
||||
import Method from '../primitive/Method';
|
||||
import createType from '../../codec/createType';
|
||||
import Method from '../../primitive/Method';
|
||||
|
||||
import Metadata from './Metadata';
|
||||
import latestParsed from './v1/latest.substrate.v1.json';
|
||||
import Metadata from '../Metadata';
|
||||
import latestParsed from './latest.substrate.v1.json';
|
||||
import rpcData from './static';
|
||||
|
||||
describe('Metadata', () => {
|
||||
describe('MetadataV1', () => {
|
||||
const metadata = new Metadata(rpcData);
|
||||
|
||||
it('decodes latest properly', () => {
|
||||
const str = JSON.stringify(metadata.toJSON());
|
||||
|
||||
console.error(str);
|
||||
console.error(metadata.getUniqTypes(true));
|
||||
// console.error(str);
|
||||
// console.error(metadata.getUniqTypes(true));
|
||||
|
||||
expect(metadata.version).toBe(1);
|
||||
expect(metadata.asV1.modules.length).not.toBe(0);
|
||||
@@ -35,11 +35,17 @@ export class MetadataStorageModifier extends EnumType<Optional | Default> {
|
||||
}
|
||||
|
||||
export class MapType extends Struct {
|
||||
private _isLinked = false;
|
||||
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
key: Type,
|
||||
value: Type
|
||||
}, value);
|
||||
|
||||
if (value && value.isLinked) {
|
||||
this._isLinked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +61,13 @@ export class MapType extends Struct {
|
||||
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 {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { OuterDispatchCall, OuterDispatchMetadata } from '../v0/Calls';
|
||||
import { EventMetadata, OuterEventMetadata, OuterEventMetadataEvent } from '../v0/Events';
|
||||
import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata } from '../v0/Modules';
|
||||
import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata, StorageFunctionType } from '../v0/Modules';
|
||||
|
||||
import MetadataV0 from '../v0/Metadata';
|
||||
import MetadataV1, { MetadataModuleV1 } from '.';
|
||||
@@ -20,7 +20,7 @@ function storageV0 (mod: MetadataModuleV1): StorageMetadata | null {
|
||||
new StorageFunctionMetadata({
|
||||
name,
|
||||
modifier: modifier.toNumber(),
|
||||
type,
|
||||
type: new StorageFunctionType(type),
|
||||
default: fallback,
|
||||
documentation: docs
|
||||
})
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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 '@polkadot/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());
|
||||
|
||||
console.error(str);
|
||||
console.error(metadata.getUniqTypes(true));
|
||||
|
||||
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;
|
||||
|
||||
// console.error(JSON.stringify(v1.toJSON()));
|
||||
|
||||
expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true));
|
||||
});
|
||||
|
||||
it('converts v2 to V0', () => {
|
||||
const v1 = metadata.asV0;
|
||||
|
||||
// console.error(JSON.stringify(v0.toJSON()));
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user