Cleanup with meta registry (#2741)

* Cleanup with meta registry

* Update packages/api-contract/src/MetaRegistry.ts

* Allow for Str type

* No .only

* Cleanups

* Registry references

* Cleanups, real registry

* Ordering

* Future-proof
This commit is contained in:
Jaco Greeff
2020-10-17 14:54:50 +02:00
committed by GitHub
parent 86cd550ad3
commit b1a8c011f4
8 changed files with 1247 additions and 199 deletions
+8 -12
View File
@@ -7,7 +7,7 @@ import * as testContracts from '../test/contracts';
const abis: Record<string, any> = { ...testContracts };
function compare (name: string, messageIds: string[]): void {
function compareInterface (name: string, messageIds: string[]): void {
try {
const inkAbi = new Abi(abis[name]);
@@ -22,15 +22,15 @@ function compare (name: string, messageIds: string[]): void {
describe('Abi', (): void => {
describe('construction', (): void => {
it('initializes from a contract ABI (flipper)', (): void => {
compare('flipper', ['flip', 'get']);
compareInterface('flipper', ['flip', 'get']);
});
it('initializes from a contract ABI (incrementer)', (): void => {
compare('incrementer', ['inc', 'get']);
compareInterface('incrementer', ['inc', 'get']);
});
it('initializes from a contract ABI (erc20)', (): void => {
compare('erc20', [
compareInterface('erc20', [
'total_supply',
'balance_of',
'allowance',
@@ -41,7 +41,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (delegator)', (): void => {
compare('delegator', [
compareInterface('delegator', [
'get',
'change',
'switch'
@@ -49,7 +49,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (dns)', (): void => {
compare('dns', [
compareInterface('dns', [
'register',
'set_address',
'transfer',
@@ -58,7 +58,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (erc721)', (): void => {
compare('erc721', [
compareInterface('erc721', [
'balance_of',
'owner_of',
'get_approved',
@@ -73,7 +73,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (multisig_plain)', (): void => {
compare('multisigPlain', [
compareInterface('multisigPlain', [
'add_owner',
'remove_owner',
'replace_owner',
@@ -87,8 +87,4 @@ describe('Abi', (): void => {
]);
});
});
it('stores base project as JSON', (): void => {
expect(new Abi(abis.dns).json).toEqual(abis.dns);
});
});
+18 -10
View File
@@ -2,12 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
import { AnyJson } from '@polkadot/types/types';
import { ChainProperties, InkConstructorSpec, InkMessageSpec } from '@polkadot/types/interfaces';
import { ChainProperties, InkConstructorSpec, InkMessageSpec, InkProject } from '@polkadot/types/interfaces';
import { AbiConstructor, AbiMessage, AbiMessageParam } from './types';
import { assert, isNumber, isObject, isString, stringCamelCase } from '@polkadot/util';
import ContractRegistry from './ContractRegistry';
import MetaRegistry from './MetaRegistry';
function findMessage <T extends AbiMessage> (list: T[], messageOrId: T | string | number): T {
const message = isNumber(messageOrId)
@@ -21,13 +21,17 @@ function findMessage <T extends AbiMessage> (list: T[], messageOrId: T | string
return message;
}
export default class Abi extends ContractRegistry {
export default class Abi {
public readonly constructors: AbiConstructor[];
public readonly json: AnyJson;
public readonly messages: AbiMessage[];
public readonly project: InkProject;
public readonly registry: MetaRegistry;
constructor (abiJson: AnyJson, chainProperties?: ChainProperties) {
const json = isString(abiJson)
? JSON.parse(abiJson) as AnyJson
@@ -35,22 +39,26 @@ export default class Abi extends ContractRegistry {
assert(isObject(json) && !Array.isArray(json) && json.metadataVersion && isObject(json.spec) && !Array.isArray(json.spec) && Array.isArray(json.spec.constructors) && Array.isArray(json.spec.messages), 'Invalid JSON ABI structure supplied, expected a recent metadata version');
super(json, chainProperties);
this.json = json;
this.registry = new MetaRegistry(chainProperties);
this.project = this.registry.createType('InkProject', json);
this.registry.setMetaTypes(this.project.types);
this.project.types.forEach((_, index) => this.registry.getMetaTypeDef(this.registry.createType('MtLookupTypeId', index + 1)));
this.constructors = this.project.spec.constructors.map((spec: InkConstructorSpec, index) =>
this._createBase(spec, index, {
this.#createBase(spec, index, {
isConstructor: true
})
);
this.messages = this.project.spec.messages.map((spec: InkMessageSpec, index): AbiMessage => {
const typeSpec = spec.returnType.unwrapOr(null);
return this._createBase(spec, index, {
return this.#createBase(spec, index, {
isMutating: spec.mutates.isTrue,
isPayable: spec.payable.isTrue,
returnType: typeSpec
? this.typeDefAt(typeSpec.type)
? this.registry.getMetaTypeDef(typeSpec.type)
: null
});
});
@@ -64,7 +72,7 @@ export default class Abi extends ContractRegistry {
return findMessage(this.messages, messageOrId);
}
private _createBase (spec: InkMessageSpec | InkConstructorSpec, index: number, add: Partial<AbiMessage> = {}): AbiMessage {
#createBase = (spec: InkMessageSpec | InkConstructorSpec, index: number, add: Partial<AbiMessage> = {}): AbiMessage => {
const identifier = spec.name.toString();
const args = spec.args.map((arg, index): AbiMessageParam => {
try {
@@ -72,7 +80,7 @@ export default class Abi extends ContractRegistry {
return {
name: stringCamelCase(arg.name.toString()),
type: this.typeDefAt(arg.type.type)
type: this.registry.getMetaTypeDef(arg.type.type)
};
} catch (error) {
console.error(`Error expanding argument ${index} in ${JSON.stringify(spec)}`);
@@ -1,58 +0,0 @@
// Copyright 2017-2020 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
/* eslint-disable @typescript-eslint/no-var-requires */
import fs from 'fs';
import path from 'path';
import ContractRegistry from './ContractRegistry';
import * as testContracts from '../test/contracts';
const abis: Record<string, any> = { ...testContracts };
function compare (name: string): void {
const inkRegistry = new ContractRegistry(abis[name]);
try {
const cmpPath = path.join(__dirname, `../test/compare/${name}.test.json`);
if (!fs.existsSync(cmpPath)) {
fs.writeFileSync(cmpPath, JSON.stringify(inkRegistry.typeDefs, null, 2), { flag: 'w' });
}
expect(inkRegistry.typeDefs).toEqual(require(cmpPath));
} catch (error) {
console.error(JSON.stringify(inkRegistry.typeDefs));
throw error;
}
}
describe('ContractRegistry', (): void => {
describe('construction', (): void => {
it('initializes from a contract ABI (flipper)', (): void => {
compare('flipper');
});
it('initializes from a contract ABI (incrementer)', (): void => {
compare('incrementer');
});
it('initializes from a contract ABI (erc20)', (): void => {
compare('erc20');
});
it('initializes from a contract ABI (dns)', (): void => {
compare('dns');
});
it('initializes from a contract ABI (erc721)', (): void => {
compare('erc721');
});
it('initializes from a contract ABI (multisig_plain)', (): void => {
compare('multisigPlain');
});
});
});
@@ -0,0 +1,70 @@
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
import fs from 'fs';
import path from 'path';
import Abi from './Abi';
import * as testContracts from '../test/contracts';
import * as userContracts from '../test/contracts/user';
const abis: Record<string, any> = { ...testContracts };
const userAbis: Record<string, any> = { ...userContracts };
function compareTypes (name: string): void {
const abi = new Abi(abis[name] || userAbis[name]);
try {
const cmpPath = path.join(__dirname, `../test/compare/${name}.test.json`);
if (!fs.existsSync(cmpPath)) {
fs.writeFileSync(cmpPath, JSON.stringify(abi.registry.metaTypeDefs, null, 2), { flag: 'w' });
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
expect(abi.registry.metaTypeDefs).toEqual(require(cmpPath));
} catch (error) {
console.error(JSON.stringify(abi.registry.metaTypeDefs));
throw error;
}
}
describe('MetaRegistry', (): void => {
describe('types', (): void => {
it('initializes from a contract ABI (flipper)', (): void => {
compareTypes('flipper');
});
it('initializes from a contract ABI (incrementer)', (): void => {
compareTypes('incrementer');
});
it('initializes from a contract ABI (erc20)', (): void => {
compareTypes('erc20');
});
it('initializes from a contract ABI (dns)', (): void => {
compareTypes('dns');
});
it('initializes from a contract ABI (erc721)', (): void => {
compareTypes('erc721');
});
it('initializes from a contract ABI (multisig_plain)', (): void => {
compareTypes('multisigPlain');
});
});
describe('user ABIs', (): void => {
it('initializes from a contract ABI (withString)', (): void => {
compareTypes('withString');
});
});
it('stores base project as JSON', (): void => {
expect(new Abi(abis.dns).json).toEqual(abis.dns);
});
});
@@ -1,8 +1,8 @@
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { ChainProperties, InkProject, MtField, MtLookupTypeId, MtType, MtTypeDefArray, MtTypeDefVariant, MtTypeDefSequence, MtTypeDefTuple, MtVariant } from '@polkadot/types/interfaces';
import { AnyJson, Registry, TypeDef, TypeDefInfo } from '@polkadot/types/types';
import { ChainProperties, MtField, MtLookupTypeId, MtType, MtTypeDefArray, MtTypeDefVariant, MtTypeDefSequence, MtTypeDefTuple, MtVariant } from '@polkadot/types/interfaces';
import { InterfaceTypes, TypeDef, TypeDefInfo } from '@polkadot/types/types';
import { assert, isUndefined } from '@polkadot/util';
import { TypeRegistry, withTypeString } from '@polkadot/types';
@@ -12,71 +12,72 @@ export function getRegistryOffset (id: MtLookupTypeId): number {
return id.toNumber() - 1;
}
export default class ContractRegistry {
public readonly typeDefs: TypeDef[] = [];
const PRIMITIVE_ALIAS: Record<string, keyof InterfaceTypes> = {
Str: 'Text'
};
public readonly registry: Registry;
export default class MetaRegistry extends TypeRegistry {
public readonly metaTypeDefs: TypeDef[] = [];
public readonly project: InkProject;
#metaTypes: MtType[] = [];
constructor (json: Record<string, AnyJson>, chainProperties?: ChainProperties) {
this.registry = new TypeRegistry();
constructor (chainProperties?: ChainProperties) {
super();
if (chainProperties) {
this.registry.setChainProperties(chainProperties);
this.setChainProperties(chainProperties);
}
this.project = this.registry.createType('InkProject', json);
// Generate TypeDefs for each provided registry type
this.project.types.forEach((_, index) => this.typeDefAt(this.registry.createType('MtLookupTypeId', index + 1)));
}
public getAbiType (id: MtLookupTypeId): MtType {
const type = this.project.types[getRegistryOffset(id)];
assert(!isUndefined(type), `getAbiType:: Unable to find ${id.toNumber()} in type values`);
return this.registry.createType('MtType', type);
public setMetaTypes (metaTypes: MtType[]): void {
this.#metaTypes = metaTypes;
}
public typeDefAt (id: MtLookupTypeId): TypeDef {
public getMetaTypeDef (id: MtLookupTypeId): TypeDef {
const offset = getRegistryOffset(id);
let typeDef = this.typeDefs[offset];
let typeDef = this.metaTypeDefs[offset];
if (!typeDef) {
typeDef = this.extractType(this.getAbiType(id), id);
typeDef = this.#extract(this.#getMetaType(id), id);
this.typeDefs[offset] = typeDef;
this.metaTypeDefs[offset] = typeDef;
// Here we protect against the following cases
// - No displayName present, these are generally known primitives
// - displayName === type, these generate circular references
// - displayName Option & type Option<...something...>
if (typeDef.displayName && !(typeDef.type === typeDef.displayName || typeDef.type.startsWith(`${typeDef.displayName}<`))) {
this.registry.register({ [typeDef.displayName]: typeDef.type });
this.register({ [typeDef.displayName]: typeDef.type });
}
}
return typeDef;
}
private extractType (inkType: MtType, id: MtLookupTypeId): TypeDef {
const path = [...inkType.path];
#getMetaType = (id: MtLookupTypeId): MtType => {
const type = this.#metaTypes[getRegistryOffset(id)];
assert(!isUndefined(type), `getMetaType:: Unable to find ${id.toNumber()} in type values`);
return this.createType('MtType', type);
}
#extract = (type: MtType, id: MtLookupTypeId): TypeDef => {
const path = [...type.path];
let typeDef: Omit<TypeDef, 'type'>;
if (inkType.path.join('::').startsWith('ink_env::types::') || inkType.def.isPrimitive) {
typeDef = this.extractPrimitive(inkType);
} else if (inkType.def.isComposite) {
typeDef = this.extractFields(inkType.def.asComposite.fields);
} else if (inkType.def.isVariant) {
typeDef = this.extractVariant(inkType.def.asVariant, id);
} else if (inkType.def.isArray) {
typeDef = this.extractArray(inkType.def.asArray);
} else if (inkType.def.isSequence) {
typeDef = this.extractSequence(inkType.def.asSequence, id);
} else if (inkType.def.isTuple) {
typeDef = this.extractTuple(inkType.def.asTuple);
if (type.path.join('::').startsWith('ink_env::types::') || type.def.isPrimitive) {
typeDef = this.#extractPrimitive(type);
} else if (type.def.isComposite) {
typeDef = this.#extractFields(type.def.asComposite.fields);
} else if (type.def.isVariant) {
typeDef = this.#extractVariant(type.def.asVariant, id);
} else if (type.def.isArray) {
typeDef = this.#extractArray(type.def.asArray);
} else if (type.def.isSequence) {
typeDef = this.#extractSequence(type.def.asSequence, id);
} else if (type.def.isTuple) {
typeDef = this.#extractTuple(type.def.asTuple);
} else {
throw new Error(`Invalid ink! type at index ${id.toString()}`);
}
@@ -92,64 +93,25 @@ export default class ContractRegistry {
? { namespace: path.map((segment) => segment.toString()).join('::') }
: {}
),
...(inkType.params.length > 0
? { params: inkType.params.map((id) => this.typeDefAt(id)) }
...(type.params.length > 0
? { params: type.params.map((id) => this.getMetaTypeDef(id)) }
: {}
),
...typeDef
});
}
private extractVariant ({ variants }: MtTypeDefVariant, id: MtLookupTypeId): Omit<TypeDef, 'type'> {
const { params, path } = this.getAbiType(id);
const specialVariant = path[0].toString();
if (specialVariant === 'Option') {
return {
info: TypeDefInfo.Option,
sub: this.typeDefAt(params[0])
};
} else if (specialVariant === 'Result') {
return {
info: TypeDefInfo.Result,
sub: params.map((param, index) => ({
name: ['Ok', 'Error'][index],
...this.typeDefAt(param)
}))
};
}
#extractArray = ({ len: length, type }: MtTypeDefArray): Omit<TypeDef, 'type'> => {
assert(!length || length.toNumber() <= 256, 'ContractRegistry: Only support for [Type; <length>], where length > 256');
return {
info: TypeDefInfo.Enum,
sub: this.extractVariantSub(variants)
info: TypeDefInfo.VecFixed,
length: length.toNumber(),
sub: this.getMetaTypeDef(type)
};
}
private extractVariantSub (variants: MtVariant[]): TypeDef[] {
const isAllUnitVariants = variants.every(({ fields }) => fields.length === 0);
if (isAllUnitVariants) {
return variants.map(({ discriminant, name }) => ({
...(
discriminant.isSome
? { ext: { discriminant: discriminant.unwrap().toNumber() } }
: {}
),
info: TypeDefInfo.Plain,
name: name.toString(),
type: 'Null'
}));
}
return variants.map(({ fields, name }) =>
withTypeString({
...this.extractFields(fields),
name: name.toString()
})
);
}
private extractFields (fields: MtField[]): Omit<TypeDef, 'type'> {
#extractFields = (fields: MtField[]): Omit<TypeDef, 'type'> => {
const [isStruct, isTuple] = fields.reduce(([isAllNamed, isAllUnnamed], { name }) => ([
isAllNamed && name.isSome,
isAllUnnamed && name.isNone
@@ -169,7 +131,7 @@ export default class ContractRegistry {
const sub = fields.map(({ name, type }) => {
return {
...this.typeDefAt(type),
...this.getMetaTypeDef(type),
...(name.isSome ? { name: name.unwrap().toString() } : {})
};
});
@@ -179,47 +141,89 @@ export default class ContractRegistry {
: { info, sub };
}
private extractArray ({ len: length, type }: MtTypeDefArray): Omit<TypeDef, 'type'> {
assert(!length || length.toNumber() <= 256, 'ContractRegistry: Only support for [Type; <length>], where length > 256');
#extractPrimitive = (type: MtType): TypeDef => {
if (type.def.isPrimitive) {
const typeStr = type.def.asPrimitive.type.toString();
return {
info: TypeDefInfo.VecFixed,
length: length.toNumber(),
sub: this.typeDefAt(type)
};
}
private extractSequence ({ type }: MtTypeDefSequence, id: MtLookupTypeId): Omit<TypeDef, 'type'> {
assert(!!type, `ContractRegistry: Invalid sequence type found at id ${id.toString()}`);
return {
info: TypeDefInfo.Vec,
sub: this.typeDefAt(type)
};
}
private extractTuple (ids: MtTypeDefTuple): Omit<TypeDef, 'type'> {
return ids.length === 1
? this.typeDefAt(ids[0])
: {
info: TypeDefInfo.Tuple,
sub: ids.map((id) => this.typeDefAt(id))
};
}
private extractPrimitive (inkType: MtType): TypeDef {
if (inkType.def.isPrimitive) {
return {
info: TypeDefInfo.Plain,
type: inkType.def.asPrimitive.type.toLowerCase()
// FIXME This should not be as a blanket toLowerCase
type: PRIMITIVE_ALIAS[typeStr] || typeStr.toLowerCase()
};
} else if (inkType.path.length > 1) {
} else if (type.path.length > 1) {
return {
info: TypeDefInfo.Plain,
type: inkType.path[inkType.path.length - 1].toString()
type: type.path[type.path.length - 1].toString()
};
}
throw new Error('Invalid primitive type');
}
#extractSequence = ({ type }: MtTypeDefSequence, id: MtLookupTypeId): Omit<TypeDef, 'type'> => {
assert(!!type, `ContractRegistry: Invalid sequence type found at id ${id.toString()}`);
return {
info: TypeDefInfo.Vec,
sub: this.getMetaTypeDef(type)
};
}
#extractTuple = (ids: MtTypeDefTuple): Omit<TypeDef, 'type'> => {
return ids.length === 1
? this.getMetaTypeDef(ids[0])
: {
info: TypeDefInfo.Tuple,
sub: ids.map((id) => this.getMetaTypeDef(id))
};
}
#extractVariant = ({ variants }: MtTypeDefVariant, id: MtLookupTypeId): Omit<TypeDef, 'type'> => {
const { params, path } = this.#getMetaType(id);
const specialVariant = path[0].toString();
if (specialVariant === 'Option') {
return {
info: TypeDefInfo.Option,
sub: this.getMetaTypeDef(params[0])
};
} else if (specialVariant === 'Result') {
return {
info: TypeDefInfo.Result,
sub: params.map((param, index) => ({
name: ['Ok', 'Error'][index],
...this.getMetaTypeDef(param)
}))
};
}
return {
info: TypeDefInfo.Enum,
sub: this.#extractVariantSub(variants)
};
}
#extractVariantSub = (variants: MtVariant[]): TypeDef[] => {
const isAllUnitVariants = variants.every(({ fields }) => fields.length === 0);
if (isAllUnitVariants) {
return variants.map(({ discriminant, name }) => ({
...(
discriminant.isSome
? { ext: { discriminant: discriminant.unwrap().toNumber() } }
: {}
),
info: TypeDefInfo.Plain,
name: name.toString(),
type: 'Null'
}));
}
return variants.map(({ fields, name }) =>
withTypeString({
...this.#extractFields(fields),
name: name.toString()
})
);
}
}
@@ -0,0 +1,247 @@
[
{
"info": 6,
"type": "u128"
},
{
"displayName": "Header",
"namespace": "ink_storage::collections::stash",
"info": 9,
"sub": [
{
"info": 6,
"type": "u32",
"name": "last_vacant"
},
{
"info": 6,
"type": "u32",
"name": "len"
},
{
"info": 6,
"type": "u32",
"name": "len_entries"
}
],
"type": "{\"last_vacant\":\"u32\",\"len\":\"u32\",\"len_entries\":\"u32\"}"
},
{
"info": 6,
"type": "u32"
},
{
"displayName": "Entry",
"namespace": "ink_storage::collections::stash",
"params": [
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
}
],
"info": 3,
"sub": [
{
"displayName": "VacantEntry",
"namespace": "ink_storage::collections::stash",
"info": 9,
"sub": [
{
"info": 6,
"type": "u32",
"name": "next"
},
{
"info": 6,
"type": "u32",
"name": "prev"
}
],
"type": "{\"next\":\"u32\",\"prev\":\"u32\"}",
"name": "Vacant"
},
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId",
"name": "Occupied"
}
],
"type": "{\"_enum\":{\"Vacant\":\"VacantEntry\",\"Occupied\":\"AccountId\"}}"
},
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
},
{
"info": 12,
"length": 32,
"sub": {
"info": 6,
"type": "u8"
},
"type": "[u8;32]"
},
{
"info": 6,
"type": "u8"
},
{
"displayName": "VacantEntry",
"namespace": "ink_storage::collections::stash",
"info": 9,
"sub": [
{
"info": 6,
"type": "u32",
"name": "next"
},
{
"info": 6,
"type": "u32",
"name": "prev"
}
],
"type": "{\"next\":\"u32\",\"prev\":\"u32\"}"
},
{
"displayName": "ValueEntry",
"namespace": "ink_storage::collections::hashmap",
"params": [
{
"info": 6,
"type": "u128"
}
],
"info": 9,
"sub": [
{
"info": 6,
"type": "u128",
"name": "value"
},
{
"info": 6,
"type": "u32",
"name": "key_index"
}
],
"type": "{\"value\":\"u128\",\"key_index\":\"u32\"}"
},
{
"displayName": "Entry",
"namespace": "ink_storage::collections::stash",
"params": [
{
"info": 10,
"sub": [
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
},
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
}
],
"type": "(AccountId, AccountId)"
}
],
"info": 3,
"sub": [
{
"displayName": "VacantEntry",
"namespace": "ink_storage::collections::stash",
"info": 9,
"sub": [
{
"info": 6,
"type": "u32",
"name": "next"
},
{
"info": 6,
"type": "u32",
"name": "prev"
}
],
"type": "{\"next\":\"u32\",\"prev\":\"u32\"}",
"name": "Vacant"
},
{
"info": 10,
"sub": [
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
},
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
}
],
"type": "(AccountId, AccountId)",
"name": "Occupied"
}
],
"type": "{\"_enum\":{\"Vacant\":\"VacantEntry\",\"Occupied\":\"(AccountId, AccountId)\"}}"
},
{
"info": 10,
"sub": [
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
},
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
}
],
"type": "(AccountId, AccountId)"
},
{
"info": 6,
"type": "Text"
},
{
"info": 6,
"type": "bool"
},
{
"displayName": "Option",
"params": [
{
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
}
],
"info": 5,
"sub": {
"displayName": "AccountId",
"namespace": "ink_env::types",
"info": 6,
"type": "AccountId"
},
"type": "Option<AccountId>"
}
]
@@ -0,0 +1,4 @@
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
export { default as withString } from './withString.json';
@@ -0,0 +1,777 @@
{
"metadataVersion": "0.1.0",
"source": {
"hash": "0xafcd0acf1a747cca1febe5bad81a6c0244a381ceb02635ab1069a1a396adda3f",
"language": "ink! 3.0.0-rc1",
"compiler": "rustc 1.49.0-nightly"
},
"contract": {
"name": "erc20",
"version": "0.1.0",
"authors": [
"[your_name] <[your_email]>"
]
},
"spec": {
"constructors": [
{
"args": [
{
"name": "initial_supply",
"type": {
"displayName": [
"Balance"
],
"type": 1
}
},
{
"name": "_name",
"type": {
"displayName": [
"String"
],
"type": 12
}
},
{
"name": "_symbol",
"type": {
"displayName": [
"String"
],
"type": 12
}
}
],
"docs": [],
"name": [
"new"
],
"selector": "0xd183512b"
}
],
"docs": [],
"events": [
{
"args": [
{
"docs": [],
"indexed": true,
"name": "from",
"type": {
"displayName": [
"Option"
],
"type": 14
}
},
{
"docs": [],
"indexed": true,
"name": "to",
"type": {
"displayName": [
"Option"
],
"type": 14
}
},
{
"docs": [],
"indexed": true,
"name": "value",
"type": {
"displayName": [
"Balance"
],
"type": 1
}
}
],
"docs": [],
"name": "Transfer"
},
{
"args": [
{
"docs": [],
"indexed": true,
"name": "owner",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"docs": [],
"indexed": true,
"name": "spender",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"docs": [],
"indexed": true,
"name": "value",
"type": {
"displayName": [
"Balance"
],
"type": 1
}
}
],
"docs": [],
"name": "Approval"
}
],
"messages": [
{
"args": [],
"docs": [],
"mutates": false,
"name": [
"total_supply"
],
"payable": false,
"returnType": {
"displayName": [
"Balance"
],
"type": 1
},
"selector": "0xdcb736b5"
},
{
"args": [],
"docs": [],
"mutates": false,
"name": [
"name"
],
"payable": false,
"returnType": {
"displayName": [
"String"
],
"type": 12
},
"selector": "0xa0a95494"
},
{
"args": [],
"docs": [],
"mutates": false,
"name": [
"symbol"
],
"payable": false,
"returnType": {
"displayName": [
"String"
],
"type": 12
},
"selector": "0x57178a4a"
},
{
"args": [
{
"name": "owner",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
}
],
"docs": [],
"mutates": false,
"name": [
"balance_of"
],
"payable": false,
"returnType": {
"displayName": [
"Balance"
],
"type": 1
},
"selector": "0x56e929b2"
},
{
"args": [
{
"name": "owner",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"name": "spender",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
}
],
"docs": [],
"mutates": false,
"name": [
"allowance"
],
"payable": false,
"returnType": {
"displayName": [
"Balance"
],
"type": 1
},
"selector": "0xf3cfff66"
},
{
"args": [
{
"name": "to",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"name": "value",
"type": {
"displayName": [
"Balance"
],
"type": 1
}
}
],
"docs": [],
"mutates": true,
"name": [
"transfer"
],
"payable": false,
"returnType": {
"displayName": [
"bool"
],
"type": 13
},
"selector": "0xfae3a09d"
},
{
"args": [
{
"name": "spender",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"name": "value",
"type": {
"displayName": [
"Balance"
],
"type": 1
}
}
],
"docs": [],
"mutates": true,
"name": [
"approve"
],
"payable": false,
"returnType": {
"displayName": [
"bool"
],
"type": 13
},
"selector": "0x03d0e114"
},
{
"args": [
{
"name": "from",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"name": "to",
"type": {
"displayName": [
"AccountId"
],
"type": 5
}
},
{
"name": "value",
"type": {
"displayName": [
"Balance"
],
"type": 1
}
}
],
"docs": [],
"mutates": true,
"name": [
"transfer_from"
],
"payable": false,
"returnType": {
"displayName": [
"bool"
],
"type": 13
},
"selector": "0xfcfb2ccd"
}
]
},
"storage": {
"struct": {
"fields": [
{
"layout": {
"cell": {
"key": "0x0000000000000000000000000000000000000000000000000000000000000000",
"ty": 1
}
},
"name": "total_supply"
},
{
"layout": {
"struct": {
"fields": [
{
"layout": {
"struct": {
"fields": [
{
"layout": {
"cell": {
"key": "0x0100000000000000000000000000000000000000000000000000000000000000",
"ty": 2
}
},
"name": "header"
},
{
"layout": {
"struct": {
"fields": [
{
"layout": {
"cell": {
"key": "0x0200000000000000000000000000000000000000000000000000000000000000",
"ty": 3
}
},
"name": "len"
},
{
"layout": {
"array": {
"cellsPerElem": 1,
"layout": {
"cell": {
"key": "0x0200000001000000000000000000000000000000000000000000000000000000",
"ty": 4
}
},
"len": 4294967295,
"offset": "0x0300000000000000000000000000000000000000000000000000000000000000"
}
},
"name": "elems"
}
]
}
},
"name": "entries"
}
]
}
},
"name": "keys"
},
{
"layout": {
"hash": {
"layout": {
"cell": {
"key": "0x0300000001000000000000000000000000000000000000000000000000000000",
"ty": 9
}
},
"offset": "0x0200000001000000000000000000000000000000000000000000000000000000",
"strategy": {
"hasher": "Blake2x256",
"postfix": "",
"prefix": "0x696e6b20686173686d6170"
}
}
},
"name": "values"
}
]
}
},
"name": "balances"
},
{
"layout": {
"struct": {
"fields": [
{
"layout": {
"struct": {
"fields": [
{
"layout": {
"cell": {
"key": "0x0300000001000000000000000000000000000000000000000000000000000000",
"ty": 2
}
},
"name": "header"
},
{
"layout": {
"struct": {
"fields": [
{
"layout": {
"cell": {
"key": "0x0400000001000000000000000000000000000000000000000000000000000000",
"ty": 3
}
},
"name": "len"
},
{
"layout": {
"array": {
"cellsPerElem": 1,
"layout": {
"cell": {
"key": "0x0400000002000000000000000000000000000000000000000000000000000000",
"ty": 10
}
},
"len": 4294967295,
"offset": "0x0500000001000000000000000000000000000000000000000000000000000000"
}
},
"name": "elems"
}
]
}
},
"name": "entries"
}
]
}
},
"name": "keys"
},
{
"layout": {
"hash": {
"layout": {
"cell": {
"key": "0x0500000002000000000000000000000000000000000000000000000000000000",
"ty": 9
}
},
"offset": "0x0400000002000000000000000000000000000000000000000000000000000000",
"strategy": {
"hasher": "Blake2x256",
"postfix": "",
"prefix": "0x696e6b20686173686d6170"
}
}
},
"name": "values"
}
]
}
},
"name": "allowances"
},
{
"layout": {
"cell": {
"key": "0x0500000002000000000000000000000000000000000000000000000000000000",
"ty": 12
}
},
"name": "name"
},
{
"layout": {
"cell": {
"key": "0x0600000002000000000000000000000000000000000000000000000000000000",
"ty": 12
}
},
"name": "symbol"
}
]
}
},
"types": [
{
"def": {
"primitive": "u128"
}
},
{
"def": {
"composite": {
"fields": [
{
"name": "last_vacant",
"type": 3
},
{
"name": "len",
"type": 3
},
{
"name": "len_entries",
"type": 3
}
]
}
},
"path": [
"ink_storage",
"collections",
"stash",
"Header"
]
},
{
"def": {
"primitive": "u32"
}
},
{
"def": {
"variant": {
"variants": [
{
"fields": [
{
"type": 8
}
],
"name": "Vacant"
},
{
"fields": [
{
"type": 5
}
],
"name": "Occupied"
}
]
}
},
"params": [
5
],
"path": [
"ink_storage",
"collections",
"stash",
"Entry"
]
},
{
"def": {
"composite": {
"fields": [
{
"type": 6
}
]
}
},
"path": [
"ink_env",
"types",
"AccountId"
]
},
{
"def": {
"array": {
"len": 32,
"type": 7
}
}
},
{
"def": {
"primitive": "u8"
}
},
{
"def": {
"composite": {
"fields": [
{
"name": "next",
"type": 3
},
{
"name": "prev",
"type": 3
}
]
}
},
"path": [
"ink_storage",
"collections",
"stash",
"VacantEntry"
]
},
{
"def": {
"composite": {
"fields": [
{
"name": "value",
"type": 1
},
{
"name": "key_index",
"type": 3
}
]
}
},
"params": [
1
],
"path": [
"ink_storage",
"collections",
"hashmap",
"ValueEntry"
]
},
{
"def": {
"variant": {
"variants": [
{
"fields": [
{
"type": 8
}
],
"name": "Vacant"
},
{
"fields": [
{
"type": 11
}
],
"name": "Occupied"
}
]
}
},
"params": [
11
],
"path": [
"ink_storage",
"collections",
"stash",
"Entry"
]
},
{
"def": {
"tuple": [
5,
5
]
}
},
{
"def": {
"primitive": "str"
}
},
{
"def": {
"primitive": "bool"
}
},
{
"def": {
"variant": {
"variants": [
{
"name": "None"
},
{
"fields": [
{
"type": 5
}
],
"name": "Some"
}
]
}
},
"params": [
5
],
"path": [
"Option"
]
}
]
}