From c9c872c736cab36a0ea376c695b02eec2110db50 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 23 Oct 2020 00:17:41 +0200 Subject: [PATCH] Expand metadata typeDefs for contracts (#2767) * Expand metada typeDefs for contracts * Adjust with actual displayName --- packages/api-contract/src/Abi.spec.ts | 23 ++++---- packages/api-contract/src/Abi.ts | 8 ++- .../api-contract/src/MetaRegistry.spec.ts | 36 ++++++------ packages/api-contract/src/MetaRegistry.ts | 57 ++++++++++++------- 4 files changed, 69 insertions(+), 55 deletions(-) diff --git a/packages/api-contract/src/Abi.spec.ts b/packages/api-contract/src/Abi.spec.ts index 8da09c5ba1..03196cc6f0 100644 --- a/packages/api-contract/src/Abi.spec.ts +++ b/packages/api-contract/src/Abi.spec.ts @@ -7,18 +7,6 @@ import * as testContracts from '../test/contracts'; const abis: Record = { ...testContracts }; -function compareInterface (name: string, messageIds: string[]): void { - try { - const inkAbi = new Abi(abis[name]); - - expect(inkAbi.messages.map(({ identifier }) => identifier)).toEqual(messageIds); - } catch (error) { - console.error(error); - - throw error; - } -} - interface JSONAbi { spec: { messages: { @@ -30,7 +18,16 @@ interface JSONAbi { describe('Abi', (): void => { Object.entries(abis).forEach(([abiName, abi]) => { it(`initializes from a contract ABI (${abiName})`, (): void => { - compareInterface(abiName, (abi as JSONAbi).spec.messages.map(({ name }) => Array.isArray(name) ? name[0] : name)); + try { + const messageIds = (abi as JSONAbi).spec.messages.map(({ name }) => Array.isArray(name) ? name[0] : name); + const inkAbi = new Abi(abis[abiName]); + + expect(inkAbi.messages.map(({ identifier }) => identifier)).toEqual(messageIds); + } catch (error) { + console.error(error); + + throw error; + } }); }); }); diff --git a/packages/api-contract/src/Abi.ts b/packages/api-contract/src/Abi.ts index 7d4df9a13a..7513c58080 100644 --- a/packages/api-contract/src/Abi.ts +++ b/packages/api-contract/src/Abi.ts @@ -47,7 +47,9 @@ export default class Abi { this.registry.setMetaTypes(this.project.types); - this.project.types.forEach((_, index) => this.registry.getMetaTypeDef(this.registry.createType('SiLookupTypeId', index + 1))); + this.project.types.forEach((_, index) => + this.registry.getMetaTypeDef({ type: this.registry.createType('SiLookupTypeId', index + 1) }) + ); this.constructors = this.project.spec.constructors.map((spec: ContractConstructorSpec, index) => this.#createMessage(spec, index, { isConstructor: true @@ -63,7 +65,7 @@ export default class Abi { isMutating: spec.mutates.isTrue, isPayable: spec.payable.isTrue, returnType: typeSpec - ? this.registry.getMetaTypeDef(typeSpec.type) + ? this.registry.getMetaTypeDef(typeSpec) : null }); }); @@ -107,7 +109,7 @@ export default class Abi { return { name: stringCamelCase(arg.name.toString()), - type: this.registry.getMetaTypeDef(arg.type.type) + type: this.registry.getMetaTypeDef(arg.type) }; } catch (error) { console.error(`Error expanding argument ${index} in ${JSON.stringify(spec)}`); diff --git a/packages/api-contract/src/MetaRegistry.spec.ts b/packages/api-contract/src/MetaRegistry.spec.ts index f3139f4712..609dc49398 100644 --- a/packages/api-contract/src/MetaRegistry.spec.ts +++ b/packages/api-contract/src/MetaRegistry.spec.ts @@ -10,29 +10,25 @@ import * as testContracts from '../test/contracts'; const abis: Record = { ...testContracts }; -function compareTypes (name: string): void { - const abi = new Abi(abis[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 => { Object.keys(abis).forEach((abiName) => { it(`initializes from a contract ABI (${abiName})`, (): void => { - compareTypes(abiName); + const abi = new Abi(abis[abiName]); + + try { + const cmpPath = path.join(__dirname, `../test/compare/${abiName}.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; + } }); }); diff --git a/packages/api-contract/src/MetaRegistry.ts b/packages/api-contract/src/MetaRegistry.ts index 5b62f345d4..956831de78 100644 --- a/packages/api-contract/src/MetaRegistry.ts +++ b/packages/api-contract/src/MetaRegistry.ts @@ -1,12 +1,17 @@ // Copyright 2017-2020 @polkadot/api-contract authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { ChainProperties, SiField, SiLookupTypeId, SiType, SiTypeDefArray, SiTypeDefVariant, SiTypeDefSequence, SiTypeDefTuple, SiVariant } from '@polkadot/types/interfaces'; +import { ContractDisplayName, ChainProperties, SiField, SiLookupTypeId, SiType, SiTypeDefArray, SiTypeDefVariant, SiTypeDefSequence, SiTypeDefTuple, SiVariant } from '@polkadot/types/interfaces'; import { InterfaceTypes, TypeDef, TypeDefInfo } from '@polkadot/types/types'; import { assert, isUndefined } from '@polkadot/util'; import { TypeRegistry, withTypeString } from '@polkadot/types'; +interface PartialTypeSpec { + readonly type: SiLookupTypeId; + readonly displayName?: ContractDisplayName; +} + // convert the offset into project-specific, index-1 export function getRegistryOffset (id: SiLookupTypeId): number { return id.toNumber() - 1; @@ -34,24 +39,38 @@ export default class MetaRegistry extends TypeRegistry { this.#siTypes = metaTypes; } - public getMetaTypeDef (id: SiLookupTypeId): TypeDef { - const offset = getRegistryOffset(id); + public getMetaTypeDef (typeSpec: PartialTypeSpec): TypeDef { + const offset = getRegistryOffset(typeSpec.type); let typeDef = this.metaTypeDefs[offset]; if (!typeDef) { - typeDef = this.#extract(this.#getMetaType(id), id); + typeDef = this.#extract(this.#getMetaType(typeSpec.type), typeSpec.type); 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.register({ [typeDef.displayName]: typeDef.type }); + if (typeSpec.displayName && typeSpec.displayName.length) { + const displayName = typeSpec.displayName[typeSpec.displayName.length - 1].toString(); + + if (!typeDef.type.startsWith(displayName)) { + typeDef = { + ...typeDef, + displayName, + type: ['Balance'].includes(displayName) + ? 'Balance' + : typeDef.type + }; } } + // 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 && !this.hasType(typeDef.displayName) && !(typeDef.type === typeDef.displayName || typeDef.type.startsWith(`${typeDef.displayName}<`))) { + this.register({ [typeDef.displayName]: typeDef.type }); + } + return typeDef; } @@ -98,7 +117,7 @@ export default class MetaRegistry extends TypeRegistry { : {} ), ...(type.params.length > 0 - ? { params: type.params.map((id) => this.getMetaTypeDef(id)) } + ? { params: type.params.map((type) => this.getMetaTypeDef({ type })) } : {} ), ...typeDef @@ -111,7 +130,7 @@ export default class MetaRegistry extends TypeRegistry { return { info: TypeDefInfo.VecFixed, length: length.toNumber(), - sub: this.getMetaTypeDef(type) + sub: this.getMetaTypeDef({ type }) }; } @@ -135,7 +154,7 @@ export default class MetaRegistry extends TypeRegistry { const sub = fields.map(({ name, type }) => { return { - ...this.getMetaTypeDef(type), + ...this.getMetaTypeDef({ type }), ...(name.isSome ? { name: name.unwrap().toString() } : {}) }; }); @@ -166,16 +185,16 @@ export default class MetaRegistry extends TypeRegistry { return { info: TypeDefInfo.Vec, - sub: this.getMetaTypeDef(type) + sub: this.getMetaTypeDef({ type }) }; } #extractTuple = (ids: SiTypeDefTuple): Omit => { return ids.length === 1 - ? this.getMetaTypeDef(ids[0]) + ? this.getMetaTypeDef({ type: ids[0] }) : { info: TypeDefInfo.Tuple, - sub: ids.map((id) => this.getMetaTypeDef(id)) + sub: ids.map((type) => this.getMetaTypeDef({ type })) }; } @@ -186,14 +205,14 @@ export default class MetaRegistry extends TypeRegistry { if (specialVariant === 'Option') { return { info: TypeDefInfo.Option, - sub: this.getMetaTypeDef(params[0]) + sub: this.getMetaTypeDef({ type: params[0] }) }; } else if (specialVariant === 'Result') { return { info: TypeDefInfo.Result, - sub: params.map((param, index) => ({ + sub: params.map((type, index) => ({ name: ['Ok', 'Error'][index], - ...this.getMetaTypeDef(param) + ...this.getMetaTypeDef({ type }) })) }; }