Generate types from new contract ABI JSON (#1320)

* generate types from registry

* cleanup

* merge, rearrange

* lint

* built out

* abi v2 creation

* throw on unions

* linting

* version handling

* split functions

* cleanup

* drop v1 support

* special types

* changes

* requested changes

* formatTYpe

* rv outdated test

* rv union test

* codeclimate

* codeclimate

* codeclimate

* codeclimate

* codeclimate

* review

* cleanup encodeTypes

* fix option sub

* TypeDefInfo.Result
This commit is contained in:
kwingram25
2019-10-08 16:31:48 +02:00
committed by GitHub
parent 64f8444d47
commit 05ec4964fe
31 changed files with 10253 additions and 271 deletions
@@ -66,7 +66,7 @@ const infoMapping: Record<TypeDefInfo, (value: TypeDef) => Constructor> = {
[TypeDefInfo.Linkage]: (value: TypeDef): Constructor => Linkage.withKey(getSubType(value)),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Null]: (value: TypeDef): Constructor => ClassOf('Null'),
[TypeDefInfo.Null]: (_: TypeDef): Constructor => ClassOf('Null'),
[TypeDefInfo.Option]: (value: TypeDef): Constructor => Option.with(getSubType(value)),
+12 -7
View File
@@ -24,7 +24,7 @@ function _decodeEnum (value: TypeDef, details: string[] | Record<string, string>
}))
: Object.entries(details).map(([name, type]): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(type || 'Null', name)
getTypeDef(type || 'Null', { name })
);
return value;
@@ -46,7 +46,7 @@ function _decodeSet (value: TypeDef, details: Record<string, number>): TypeDef {
// decode a struct, set or enum
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function _decodeStruct (value: TypeDef, type: string, subType: string): TypeDef {
function _decodeStruct (value: TypeDef, type: string, _: string): TypeDef {
const parsed = JSON.parse(type);
const keys = Object.keys(parsed);
@@ -58,7 +58,7 @@ function _decodeStruct (value: TypeDef, type: string, subType: string): TypeDef
value.sub = keys.map((name): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(parsed[name], name)
getTypeDef(parsed[name], { name })
);
return value;
@@ -66,7 +66,7 @@ function _decodeStruct (value: TypeDef, type: string, subType: string): TypeDef
// decode a fixed vector, e.g. [u8;32]
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function _decodeFixedVec (value: TypeDef, type: string, subType: string): TypeDef {
function _decodeFixedVec (value: TypeDef, type: string, _: string): TypeDef {
const [vecType, _vecLen] = type.substr(1, type.length - 2).split(';');
const vecLen = parseInt(_vecLen.trim(), 10);
@@ -79,7 +79,7 @@ function _decodeFixedVec (value: TypeDef, type: string, subType: string): TypeDe
}
// decode a tuple
function _decodeTuple (value: TypeDef, type: string, subType: string): TypeDef {
function _decodeTuple (value: TypeDef, _: string, subType: string): TypeDef {
value.sub = typeSplit(subType).map((inner): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(inner)
@@ -118,10 +118,15 @@ function extractSubType (type: string, [start, end]: [string, string, TypeDefInf
return type.substr(start.length, type.length - start.length - end.length);
}
export function getTypeDef (_type: string, name?: string): TypeDef {
interface TypeDefOptions {
name?: string;
displayName?: string;
}
export function getTypeDef (_type: string, { name, displayName }: TypeDefOptions = {}): TypeDef {
// create the type via Type, allowing types to be sanitized
const type = sanitize(_type);
const value: TypeDef = { info: TypeDefInfo.Plain, name, type };
const value: TypeDef = { info: TypeDefInfo.Plain, displayName, name, type };
const nested = nestedExtraction.find((nested): boolean =>
hasWrapper(type, nested)
+112 -1
View File
@@ -33,11 +33,122 @@ export interface TypeDefExtVecFixed {
type: string;
}
export interface TypeDefExtEnumDiscriminant {
discriminant: number;
}
export interface TypeDef {
info: TypeDefInfo;
index?: number;
ext?: TypeDefExtVecFixed; // add additional here as required
displayName?: string;
ext?: TypeDefExtVecFixed | TypeDefExtEnumDiscriminant; // add additional here as required
name?: string;
namespace?: string;
params?: TypeDef[];
type: string;
sub?: TypeDef | TypeDef[];
}
export type TypeIndex = number;
export type StringIndex = number;
export enum MetaTypeInfo {
BuiltinPlain,
BuiltinTuple,
BuiltinArray,
Enum,
ClikeEnum,
Struct,
TupleStruct,
Null
}
export type MetaTypeIdPrimitive = string;
export type MetaTypeIdTuple = TypeIndex[];
export interface MetaTypeIdArray {
'array.len': number;
'array.type': TypeIndex;
}
export interface MetaTypeIdCustom {
'custom.name': StringIndex;
'custom.namespace'?: StringIndex[];
'custom.params'?: TypeIndex[];
}
export type MetaTypeId = MetaTypeIdPrimitive | MetaTypeIdTuple | MetaTypeIdArray | MetaTypeIdCustom;
// MetaTypeIdPrimitive | MetaTypeIdTuple | MetaTypeIdArray | MetaTypeIdCustom;
// export type MetaTypeId = Record<string, any>;
export interface MetaTypeDefClikeEnumVariant {
name: StringIndex;
discriminant: number;
}
export interface MetaTypeDefClikeEnum {
'clike_enum.variants': MetaTypeDefClikeEnumVariant[];
}
export type MetaTypeDefBuiltIn = 'builtin';
export interface MetaTypeDefEnumVariantUnit {
'unit_variant.name': StringIndex;
}
export interface MetaTypeDefEnumVariantTupleStruct {
'tuple_struct_variant.name': StringIndex;
'tuple_struct_variant.types': TypeIndex[];
}
export interface MetaTypeDefEnumVariantStruct {
'struct_variant.name': StringIndex;
'struct_variant.fields': MetaTypeDefStructField[];
}
export type MetaTypeDefEnumVariant = MetaTypeDefEnumVariantUnit | MetaTypeDefEnumVariantTupleStruct | MetaTypeDefEnumVariantStruct;
export interface MetaTypeDefEnum {
'enum.variants': MetaTypeDefEnumVariant[];
}
export interface MetaTypeDefStructField {
name: StringIndex;
type: TypeIndex;
}
export type MetaTypeDefUnionField = MetaTypeDefStructField;
export interface MetaTypeDefStruct {
'struct.fields': MetaTypeDefStructField[];
}
export interface MetaTypeDefTupleStruct {
'tuple_struct.types': TypeIndex[];
}
export interface MetaTypeDefUnion {
'union.fields': MetaTypeDefUnionField[];
}
export type MetaTypeDef = MetaTypeDefBuiltIn | MetaTypeDefClikeEnum | MetaTypeDefEnum | MetaTypeDefStruct | MetaTypeDefTupleStruct | MetaTypeDefUnion;
export interface MetaType {
def: MetaTypeDef;
id: MetaTypeId | null;
}
export enum MetaRegistryItem {
String,
Type,
TypeDef
}
export interface MetaRegistryJson {
registry: {
strings: string[];
types: MetaType[];
};
}
+1
View File
@@ -25,3 +25,4 @@ export { default as UInt } from './UInt';
// Type management helper functions
export * from './create';
export * from './utils/encodeTypes';
@@ -0,0 +1,171 @@
// 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 { TypeDef, TypeDefInfo, TypeDefExtVecFixed } from '../types';
import { assert } from '@polkadot/util';
import { getTypeDef } from '../create';
const SPECIAL_TYPES = ['AccountId', 'AccountIndex', 'Address', 'Balance'];
export function paramsNotation (outer: string, inner?: string | any[], transform?: (_: any) => string): string {
let array;
if (inner) {
array = Array.isArray(inner) ? inner : [inner];
if (transform) {
array = array.map(transform);
}
}
return `${outer}${array ? `<${array.join(', ')}>` : ''}`;
}
class TypeEncoder {
private static enum (typeDef: Pick<TypeDef, any>): string {
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Enum type');
const sub = typeDef.sub as TypeDef[];
const isClikeEnum = sub.reduce(
(bool: boolean, { type }: TypeDef): boolean => bool && type === 'Null',
true
);
if (isClikeEnum) {
return `[${
sub
.map(({ name }: TypeDef): string => `"${name}"`)
.join(', ')
}]`;
}
return this.subTypes(sub, true);
}
private static struct (typeDef: Pick<TypeDef, any>): string {
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Struct type');
const sub = typeDef.sub as TypeDef[];
return this.subTypes(sub);
}
private static tuple (typeDef: Pick<TypeDef, any>): string {
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Tuple type');
const sub = typeDef.sub as TypeDef[];
return `(${
sub
.map((type: TypeDef): string => TypeEncoder.withParams(type))
.join(', ')
})`;
}
private static vecFixed (typeDef: Pick<TypeDef, any>): string {
assert(typeDef.ext, 'Unable to encode VecFixed type');
const { type, length } = typeDef.ext as TypeDefExtVecFixed;
return `[${
this.withParams(getTypeDef(type))
};${
length
}]`;
}
private static subTypes (sub: TypeDef[], asEnum?: boolean): string {
return `{ ${asEnum ? '"_enum": { ' : ''}${
sub
.map((type: TypeDef): string => `"${type.name}": "${this.withParams(type)}"`)
.join(', ')
}} }`;
}
private static withParams (typeDef: Pick<TypeDef, any>, outer = typeDef.displayName || typeDef.type): string {
const { params } = typeDef;
return paramsNotation(
outer,
params,
(param: TypeDef) => TypeEncoder.display(param)
);
}
public static encode (typeDef: Pick<TypeDef, any>): string {
switch (typeDef.info) {
case TypeDefInfo.Null: {
return '()';
}
case TypeDefInfo.Plain: {
return typeDef.displayName || typeDef.type;
}
case TypeDefInfo.Compact: {
return TypeEncoder.withParams(typeDef, 'Compact');
}
case TypeDefInfo.DoubleMap: {
return TypeEncoder.withParams(typeDef, 'DoubleMap');
}
case TypeDefInfo.Linkage: {
return TypeEncoder.withParams(typeDef, 'Linkage');
}
case TypeDefInfo.Option: {
return TypeEncoder.withParams(typeDef, 'Option');
}
case TypeDefInfo.Result: {
return TypeEncoder.withParams(typeDef, 'Result');
}
case TypeDefInfo.Vec: {
return TypeEncoder.withParams(typeDef, 'Vec');
}
case TypeDefInfo.Enum: {
return TypeEncoder.enum(typeDef);
}
case TypeDefInfo.Struct: {
return TypeEncoder.struct(typeDef);
}
case TypeDefInfo.Tuple: {
return TypeEncoder.tuple(typeDef);
}
case TypeDefInfo.VecFixed: {
return TypeEncoder.vecFixed(typeDef);
}
default: {
throw new Error(`Cannot encode type: ${typeDef}.`);
}
}
}
public static display (typeDef: Pick<TypeDef, any>): string {
if (typeDef.displayName) {
return TypeEncoder.withParams(typeDef);
}
switch (typeDef.info) {
case TypeDefInfo.Struct:
case TypeDefInfo.Enum:
return TypeEncoder.withParams(typeDef);
default:
return TypeEncoder.encode(typeDef);
}
}
}
export function encodeType (typeDef: Pick<TypeDef, any>): string {
return TypeEncoder.encode(typeDef);
}
export function displayType (typeDef: Pick<TypeDef, any>): string {
return TypeEncoder.display(typeDef);
}
export function withTypeString (typeDef: Pick<TypeDef, any>): Pick<TypeDef, any> {
return {
...typeDef,
type: SPECIAL_TYPES.includes(typeDef.name)
? typeDef.name
: encodeType(typeDef)
};
}
+1
View File
@@ -5,4 +5,5 @@
export { default as compareArray } from './compareArray';
export { default as compareMap } from './compareMap';
export { default as decodeU8a } from './decodeU8a';
export * from './encodeTypes';
export * from './mapToTypeMap';
+1
View File
@@ -23,3 +23,4 @@ assertSingletonPackage('@polkadot/types');
export * from './codec';
export * from './index.types';
export { formatType } from './scripts/util/formatting';
@@ -234,7 +234,7 @@ function generateInterfaces ({ types }: { types: Record<string, any> }, imports:
};
return Object.entries(types).map(([name, type]): [string, string] => {
const def = getTypeDef(isString(type) ? type.toString() : JSON.stringify(type), name);
const def = getTypeDef(isString(type) ? type.toString() : JSON.stringify(type), { name });
return [name, generators[def.info](def, imports)];
});
+13 -7
View File
@@ -5,6 +5,7 @@
import { TypeDef, TypeDefInfo } from '../../codec/types';
import { getTypeDef } from '../../codec/create';
import { paramsNotation } from '../../codec/utils';
import { setImports, TypeImports } from './imports';
export const HEADER = '// Auto-generated via `yarn build:interfaces`, do not edit\n/* eslint-disable @typescript-eslint/no-empty-interface */\n\n';
@@ -37,7 +38,7 @@ export function exportType (name = '', base: string): string {
* Given the inner `T`, return a `Compact<T>` string
*/
export function formatCompact (inner: string): string {
return `Compact<${inner}>`;
return paramsNotation('Compact', inner);
}
/**
@@ -51,28 +52,33 @@ export function formatResult (innerOk: string, innerError: string): string {
* Given the inner `T`, return a `Option<T>` string
*/
export function formatOption (inner: string): string {
return `Option<${inner}>`;
return paramsNotation('Option', inner);
}
/**
* Given the inners `T[]`, return a `ITuple<...T>` string
*/
export function formatTuple (inners: string[]): string {
return `ITuple<[${inners.join(', ')}]>`;
return paramsNotation('ITuple', `[${inners.join(', ')}]`);
}
/**
* Given the inner `T`, return a `Vec<T>` string
*/
export function formatVec (inner: string): string {
return `Vec<${inner}>`;
return paramsNotation('Vec', inner);
}
/**
* Correctly format a given type
*/
export function formatType (type: string, imports: TypeImports): string {
const typeDef = getTypeDef(type);
export function formatType (type: string | TypeDef, imports: TypeImports): string {
let typeDef;
if (typeof type === 'string') {
typeDef = getTypeDef(type);
} else {
typeDef = type;
}
switch (typeDef.info) {
case TypeDefInfo.Compact: {
@@ -82,7 +88,7 @@ export function formatType (type: string, imports: TypeImports): string {
return formatOption(formatType((typeDef.sub as TypeDef).type, imports));
}
case TypeDefInfo.Plain: {
return type;
return typeDef.type;
}
case TypeDefInfo.Vec: {
return formatVec(formatType((typeDef.sub as TypeDef).type, imports));