Revert "Move contracts into api-contract"

This reverts commit af0e8cf07a.
This commit is contained in:
Jaco Greeff
2019-06-14 09:06:57 +02:00
parent af0e8cf07a
commit 7bdf0fbbe5
24 changed files with 312 additions and 610 deletions
+82
View File
@@ -0,0 +1,82 @@
// 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 TypesAbi from '../test/abi/types.json';
import ContractAbi, { ContractABIFn$Arg } from './ContractAbi';
describe('ContractAbi', () => {
let abi: ContractAbi;
beforeAll(() => {
abi = new ContractAbi(TypesAbi);
});
function check (method: string, args: Array<ContractABIFn$Arg>, type: string | null): void {
const fn = abi.messages[method];
expect(fn.args).toEqual(args);
expect(fn.type).toEqual(type);
}
it('has the primitive method with args & return', () => {
check(
'primitive',
[
{ name: 'bool', type: 'bool' },
{ name: 'u32', type: 'u32' }
],
'bool'
);
});
it('has the vector method with args & return', () => {
check(
'vector',
[
{ name: 'vecU8', type: 'Vec<u8>' },
{ name: 'vecU32', type: 'Vec<u32>' }
],
'Vec<bool>'
);
});
it('has the vector_fixed method with args & return', () => {
check(
'vectorFixed',
[
{ name: 'vecU8Length32', type: '[u8;32]' }
],
'[u32;8]'
);
});
it('has the option method with args & return', () => {
check(
'option',
[
{ name: 'optionU32', type: 'Option<u32>' }
],
'Option<bool>'
);
});
it('has the result method with return (empty)', () => {
check(
'result',
[],
'()'
);
});
it('allows for nested args', () => {
check(
'nested',
[
{ name: 'optionVec', type: 'Option<Vec<u32>>' }
],
'Vec<(u32,u64)>'
);
});
});
+193
View File
@@ -0,0 +1,193 @@
// 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 { CodecArg, Constructor } from './types';
import { assert, isNumber, isNull, isString, isUndefined, stringCamelCase, isObject } from '@polkadot/util';
import Compact from './codec/Compact';
import { createClass } from './codec/createType';
export type ContractABITypes$Struct = {
'Option<T>'?: {
T: ContractABITypes
},
'Result<T,E>'?: {
T: ContractABITypes,
E: ContractABITypes
},
'Vec<T>'?: {
T: ContractABITypes
},
'[T;n]'?: {
T: ContractABITypes,
n: number
}
};
export type ContractABITypes = string | ContractABITypes$Struct | Array<string | ContractABITypes$Struct>;
export type ContractABIArg = {
name: string,
type: ContractABITypes
};
export type ContractABIMethodBase = {
args: Array<ContractABIArg>
};
export type ContractABIMethod = ContractABIMethodBase & {
mutates?: boolean,
name: string,
selector: number,
return_type: ContractABITypes | null
};
export type ContractABI = {
deploy: ContractABIMethodBase,
messages: Array<ContractABIMethod>,
name: string
};
export interface ContractABIFn$Arg {
name: string;
type: string;
}
export interface ContractABIFn {
(...args: Array<CodecArg>): Uint8Array;
args: Array<ContractABIFn$Arg>;
isConstant: boolean;
type: string | null;
}
export interface Contract {
abi: ContractABI;
deploy: ContractABIFn;
messages: {
[index: string]: ContractABIFn
};
}
export function validateArgs (name: string, args: Array<ContractABIArg>): void {
assert(Array.isArray(args), `Expected 'args' to exist on ${name}`);
args.forEach((arg) => {
const unknownKeys = Object.keys(arg).filter((key) => !['name', 'type'].includes(key));
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for ${name}`);
assert(isString(arg.name), `${name} args should have valid name `);
assert(isString(arg.type) || isObject(arg.type), `${name} args should have valid type`);
});
}
export function validateDeploy ({ deploy }: ContractABI): void {
const unknownKeys = Object.keys(deploy).filter((key) => !['args'].includes(key));
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI deploy`);
validateArgs('deploy', deploy.args);
}
export function validateMethods ({ messages }: ContractABI): void {
messages.forEach((method) => {
const unknownKeys = Object.keys(method).filter((key) => !['args', 'mutates', 'name', 'selector', 'return_type'].includes(key));
assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for messages.${method.name}`);
assert(isString(method.name), `Expected name for messages.${method.name}`);
assert(isNumber(method.selector), `Expected selector for messages.${method.name}`);
assert(isNull(method.return_type) || isString(method.return_type) || isObject(method.return_type), `Expected return_type for messages.${method.name}`);
validateArgs(`messages.${method.name}`, method.args);
});
}
export function validateAbi (abi: ContractABI): void {
const unknownKeys = Object.keys(abi).filter((key) => !['deploy', 'messages', 'name'].includes(key));
assert(unknownKeys.length === 0, `Unknown fields ${unknownKeys.join(', ')} found in ABI`);
assert(abi.deploy && abi.messages && isString(abi.name), 'ABI should have deploy, messages & name sections');
validateDeploy(abi);
validateMethods(abi);
}
export default class ContractAbi implements Contract {
abi: ContractABI;
deploy: ContractABIFn;
messages: { [index: string]: ContractABIFn } = {};
constructor (abi: ContractABI) {
validateAbi(abi);
this.abi = abi;
this.deploy = this._createEncoded('deploy', abi.deploy);
abi.messages.forEach((method) => {
const name = stringCamelCase(method.name);
this.messages[name] = this._createEncoded(`messages.${name}`, method);
});
}
private _convertType (type: ContractABITypes): string {
if (isString(type)) {
return type;
} else if (Array.isArray(type)) {
return `(${type.map((type) => this._convertType(type)).join(',')})`;
} else if (type['Option<T>']) {
return `Option<${this._convertType(type['Option<T>'].T)}>`;
} else if (type['Result<T,E>']) {
return `()`; // Result is not supported, but only applicable for returns
} else if (type['Vec<T>']) {
return `Vec<${this._convertType(type['Vec<T>'].T)}>`;
} else if (type['[T;n]']) {
return `[${this._convertType(type['[T;n]'].T)};${type['[T;n]'].n}]`;
}
throw new Error(`Unknown type specified ${JSON.stringify(type)}`);
}
private _createClazz (args: Array<ContractABIFn$Arg>, baseDef: { [index: string]: string }): Constructor {
return createClass(
JSON.stringify(
args.reduce((base: { [index: string]: string }, { name, type }) => {
base[name] = type;
return base;
}, baseDef)
)
);
}
private _createEncoded (name: string, method: Partial<ContractABIMethod> & ContractABIMethodBase): ContractABIFn {
const args: Array<ContractABIFn$Arg> = method.args.map(({ name, type }) => ({
name: stringCamelCase(name),
type: this._convertType(type)
}));
const Clazz = this._createClazz(args, isUndefined(method.selector) ? {} : { __selector: 'u32' });
const baseStruct: { [index: string]: any } = { __selector: method.selector };
const encoder = (...params: Array<CodecArg>): Uint8Array => {
assert(params.length === args.length, `Expected ${args.length} arguments to contract ${name}, found ${params.length}`);
const u8a = new Clazz(
args.reduce((mapped, { name }, index) => {
mapped[name] = params[index];
return mapped;
}, { ...baseStruct })
).toU8a();
return Compact.addLengthPrefix(u8a);
};
const fn = (encoder as ContractABIFn);
fn.args = args;
fn.isConstant = !(method.mutates || false);
fn.type = method.return_type ? this._convertType(method.return_type) : null;
return fn;
}
}
+1
View File
@@ -9,5 +9,6 @@ import { assertSingletonPackage } from '@polkadot/util';
assertSingletonPackage('@polkadot/types');
export { default as ContractAbi } from './ContractAbi';
export * from './codec';
export * from './index.types';
+142
View File
@@ -0,0 +1,142 @@
{
"name": "Types",
"deploy": {
"args": []
},
"messages": [
{
"name": "primitive",
"selector": 1111,
"mutates": true,
"args": [
{
"name": "bool",
"type": "bool"
},
{
"name": "u32",
"type": "u32"
}
],
"return_type": "bool"
},
{
"name": "vector",
"selector": 2222,
"mutates": false,
"args": [
{
"name": "Vec_u8",
"type": {
"Vec<T>": {
"T": "u8"
}
}
},
{
"name": "Vec_u32",
"type": {
"Vec<T>": {
"T": "u32"
}
}
}
],
"return_type": {
"Vec<T>": {
"T": "bool"
}
}
},
{
"name": "vector_fixed",
"selector": 3333,
"mutates": false,
"args": [
{
"name": "vec_u8_length_32",
"type": {
"[T;n]": {
"T": "u8",
"n": 32
}
}
}
],
"return_type": {
"[T;n]": {
"T": "u32",
"n": 8
}
}
},
{
"name": "option",
"selector": 4444,
"mutates": false,
"args": [
{
"name": "Option_u32",
"type": {
"Option<T>": {
"T": "u32"
}
}
}
],
"return_type": {
"Option<T>": {
"T": "bool"
}
}
},
{
"name": "tuple",
"selector": 5555,
"mutates": false,
"args": [
{
"name": "Tuple_u32_u64",
"type": ["u32", "u64"]
}
],
"return_type": ["bool"]
},
{
"name": "result",
"selector": 666,
"mutates": false,
"args": [],
"return_type": {
"Result<T,E>": {
"T": "u32",
"E": "u32"
}
}
},
{
"name": "nested",
"selector": 987654321,
"mutates": false,
"args": [
{
"name": "option_vec",
"type": {
"Option<T>": {
"T": {
"Vec<T>": {
"T": "u32"
}
}
}
}
}
],
"return_type": {
"Vec<T>": {
"T": ["u32", "u64"]
}
}
}
]
}