Expand metadata typeDefs for contracts (#2767)

* Expand metada typeDefs for contracts

* Adjust with actual displayName
This commit is contained in:
Jaco Greeff
2020-10-23 00:17:41 +02:00
committed by GitHub
parent 8394b7fb5a
commit c9c872c736
4 changed files with 69 additions and 55 deletions
+10 -13
View File
@@ -7,18 +7,6 @@ import * as testContracts from '../test/contracts';
const abis: Record<string, any> = { ...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;
}
});
});
});
+5 -3
View File
@@ -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)}`);
+16 -20
View File
@@ -10,29 +10,25 @@ import * as testContracts from '../test/contracts';
const abis: Record<string, any> = { ...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;
}
});
});
+38 -19
View File
@@ -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<TypeDef, 'type'> => {
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 })
}))
};
}