ABI selector updates (#1517)

This commit is contained in:
Jaco Greeff
2019-10-26 13:17:36 +03:00
committed by kwingram25
parent 3ba2bbd581
commit 6e96fd6a55
2 changed files with 38 additions and 9 deletions
+36 -8
View File
@@ -4,13 +4,36 @@
import { CodecArg, TypeDef } from '@polkadot/types/types';
import { ContractABIArgBasePre, ContractABIContract, ContractABIContractPre, ContractABIEvent, ContractABIEventPre, ContractABIFn, ContractABIFnArg, ContractABIMethod, ContractABIMethodBase, ContractABIMethodPre, ContractABI, ContractABIPre, ContractABIRange, ContractABIRangePre, ContractABIStorage, ContractABIStorageLayout, ContractABIStorageLayoutPre, ContractABIStoragePre, ContractABIStorageStruct, ContractABIStorageStructPre, ContractABITypePre } from './types';
import { Compact } from '@polkadot/types';
import { Compact, u32, createType } from '@polkadot/types';
import { assert, isNumber, isString, isNull, isObject, isUndefined, stringCamelCase } from '@polkadot/util';
import { assert, hexToU8a, isNumber, isString, isNull, isObject, isUndefined, stringCamelCase, isHex, hexToNumber } from '@polkadot/util';
import MetaRegistry from './MetaRegistry';
import { createArgClass } from './method';
// parse a selector, this can be a number (older) or of [<hex>, <hex>, ...]. However,
// just catch everything (since this is now non-standard for u32 anyway)
function parseSelector (fnname: string, input: number | string | number[] | string[]): u32 {
if (isNumber(input)) {
return createType('u32', input);
} else if (isHex(input)) {
return createType('u32', hexToU8a(input));
} else if (Array.isArray(input)) {
assert(input.length === 4, `${fnname}: Invalid selector length`);
return createType('u32', Uint8Array.from(
// the as number[] is here to pacify TS, it doesn't quite know how to handle the cb
(input as number[]).map((value: string | number): number =>
isHex(value)
? hexToNumber(value.toString())
: value
)
));
}
throw new Error(`${fnname}: Unable to parse selector`);
}
export default class ContractRegistry extends MetaRegistry {
// Contract ABI helpers
public validateArgs (name: string, args: ContractABIArgBasePre[]): void {
@@ -38,22 +61,27 @@ export default class ContractRegistry extends MetaRegistry {
public validateMethods ({ contract: { messages } }: ContractABIPre): void {
messages.forEach((method): void => {
const unknownKeys = Object.keys(method).filter((key): boolean => !['args', 'docs', 'mutates', 'name', 'selector', 'return_type'].includes(key));
const fnname = `messages.${method.name}`;
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for messages.${method.name}`);
const { name, selector, return_type: returnType } = method;
assert(isNumber(name) && isString(this.stringAt(name)), `Expected name for messages.${method.name}`);
assert(isNumber(selector), `Expected selector for messages.${method.name}`);
assert(isNull(returnType) || (isNumber(returnType.ty) && isObject(this.typeDefAt(returnType.ty))), `Expected return_type for messages.${method.name}`);
this.validateArgs(`messages.${method.name}`, method.args);
const { name, selector, return_type: returnType } = method;
assert(isNumber(name) && isString(this.stringAt(name)), `Expected name for ${fnname}`);
assert(isNull(returnType) || (isNumber(returnType.ty) && isObject(this.typeDefAt(returnType.ty))), `Expected return_type for ${fnname}`);
parseSelector(fnname, selector);
this.validateArgs(fnname, method.args);
});
}
public validateAbi (abi: ContractABIPre): void {
const unknownKeys = Object.keys(abi.contract).filter((key): boolean => !['constructors', 'docs', 'events', 'messages', 'name'].includes(key));
assert(unknownKeys.length === 0, `Unknown fields ${unknownKeys.join(', ')} found in ABI`);
const { contract: { constructors, messages, name } } = abi;
assert(constructors && messages && isString(this.stringAt(name)), 'ABI should have constructors, messages & name sections');
this.validateConstructors(abi);
@@ -69,7 +97,7 @@ export default class ContractRegistry extends MetaRegistry {
};
});
const Clazz = createArgClass(args, isUndefined(method.selector) ? {} : { __selector: 'u32' });
const baseStruct: { [index: string]: any } = { __selector: method.selector };
const baseStruct: { [index: string]: any } = { __selector: isUndefined(method.selector) ? undefined : parseSelector(name, method.selector) };
const encoder = (...params: CodecArg[]): Uint8Array => {
assert(params.length === args.length, `Expected ${args.length} arguments to contract ${name}, found ${params.length}`);
+2 -1
View File
@@ -121,7 +121,8 @@ export interface ContractABIEvent {
}
export interface ContractABIRangeBase {
'range.offset': number[];
// can be number[] (old) or hex (new)
'range.offset': number[] | string;
'range.len': number;
}