Rename contract helpers (#2708)

* Rename contract helpers

* Allow Buffer input for code

* Parse json strings

* Revert #typeDefs

* Allow lookup on name

* constsitent string ids

* Read only via address

* Move .json

* Extract findMessage

* Naming
This commit is contained in:
Jaco Greeff
2020-10-12 13:21:52 +02:00
committed by GitHub
parent fda1924577
commit 1101826a05
11 changed files with 59 additions and 61 deletions
+12 -12
View File
@@ -1,15 +1,13 @@
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { TypeRegistry } from '@polkadot/types';
import Abi from './Abi';
import * as testContracts from '../test/contracts';
const abis: Record<string, any> = { ...testContracts };
function compare (name: string, registry: TypeRegistry, messageIds: string[]): void {
function compare (name: string, messageIds: string[]): void {
try {
const inkAbi = new Abi(abis[name]);
@@ -23,18 +21,16 @@ function compare (name: string, registry: TypeRegistry, messageIds: string[]): v
describe('Abi', (): void => {
describe('construction', (): void => {
const registry = new TypeRegistry();
it('initializes from a contract ABI (flipper)', (): void => {
compare('flipper', registry, ['flip', 'get']);
compare('flipper', ['flip', 'get']);
});
it('initializes from a contract ABI (incrementer)', (): void => {
compare('incrementer', registry, ['inc', 'get']);
compare('incrementer', ['inc', 'get']);
});
it('initializes from a contract ABI (erc20)', (): void => {
compare('erc20', registry, [
compare('erc20', [
'total_supply',
'balance_of',
'allowance',
@@ -45,7 +41,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (delegator)', (): void => {
compare('delegator', registry, [
compare('delegator', [
'get',
'change',
'switch'
@@ -53,7 +49,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (dns)', (): void => {
compare('dns', registry, [
compare('dns', [
'register',
'set_address',
'transfer',
@@ -62,7 +58,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (erc721)', (): void => {
compare('erc721', registry, [
compare('erc721', [
'balance_of',
'owner_of',
'get_approved',
@@ -77,7 +73,7 @@ describe('Abi', (): void => {
});
it('initializes from a contract ABI (multisig_plain)', (): void => {
compare('multisigPlain', registry, [
compare('multisigPlain', [
'add_owner',
'remove_owner',
'replace_owner',
@@ -91,4 +87,8 @@ describe('Abi', (): void => {
]);
});
});
it('stores base project as JSON', (): void => {
expect(new Abi(abis.dns).json).toEqual(abis.dns);
});
});
+27 -18
View File
@@ -5,18 +5,39 @@ import { AnyJson } from '@polkadot/types/types';
import { InkConstructorSpec, InkMessageSpec } from '@polkadot/types/interfaces';
import { AbiConstructor, AbiMessage, AbiMessageParam } from './types';
import { assert, isNumber, isObject, stringCamelCase } from '@polkadot/util';
import { assert, isNumber, isObject, isString, stringCamelCase } from '@polkadot/util';
import ContractRegistry from './ContractRegistry';
function findMessage <T extends AbiMessage> (list: T[], messageOrId: T | string | number): T {
const message = isNumber(messageOrId)
? list[messageOrId]
: isString(messageOrId)
? list.find(({ identifier }: T) => identifier === messageOrId.toString())
: messageOrId;
assert(message, `Attempted to call an invalid contract interface, ${JSON.stringify(messageOrId)}`);
return message;
}
export default class Abi extends ContractRegistry {
public readonly constructors: AbiConstructor[];
public readonly json: AnyJson;
public readonly messages: AbiMessage[];
constructor (json: AnyJson) {
constructor (abiJson: AnyJson) {
const json = isString(abiJson)
? JSON.parse(abiJson) as AnyJson
: abiJson;
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);
this.json = json;
this.constructors = this.project.spec.constructors.map((spec: InkConstructorSpec, index) =>
this._createBase(spec, index, {
isConstructor: true
@@ -35,24 +56,12 @@ export default class Abi extends ContractRegistry {
});
}
public findConstructor (constructorOrIndex: AbiConstructor | number): AbiConstructor {
const message = isNumber(constructorOrIndex)
? this.constructors[constructorOrIndex]
: constructorOrIndex;
assert(message, 'Attempted to call an invalid contract message');
return message;
public findConstructor (constructorOrId: AbiConstructor | string | number): AbiConstructor {
return findMessage(this.constructors, constructorOrId);
}
public findMessage (messageOrIndex: AbiMessage | number): AbiMessage {
const message = isNumber(messageOrIndex)
? this.messages[messageOrIndex]
: messageOrIndex;
assert(message, 'Attempted to call an invalid contract message');
return message;
public findMessage (messageOrId: AbiMessage | string | number): AbiMessage {
return findMessage(this.messages, messageOrId);
}
private _createBase (spec: InkMessageSpec | InkConstructorSpec, index: number, add: Partial<AbiMessage> = {}): AbiMessage {
@@ -54,11 +54,5 @@ describe('ContractRegistry', (): void => {
it('initializes from a contract ABI (multisig_plain)', (): void => {
compare('multisigPlain');
});
it('stores base project as JSON', (): void => {
const inkRegistry = new ContractRegistry(abis.dns);
expect(inkRegistry.json).toEqual(abis.dns);
});
});
});
@@ -4,7 +4,7 @@
import { InkProject, MtField, MtLookupTypeId, MtType, MtTypeDefArray, MtTypeDefVariant, MtTypeDefSequence, MtTypeDefTuple, MtVariant } from '@polkadot/types/interfaces';
import { AnyJson, Registry, TypeDef, TypeDefInfo } from '@polkadot/types/types';
import { assert, isObject, isUndefined } from '@polkadot/util';
import { assert, isUndefined } from '@polkadot/util';
import { TypeRegistry, withTypeString } from '@polkadot/types';
// convert the offset into project-specific, index-1
@@ -13,19 +13,14 @@ export function getRegistryOffset (id: MtLookupTypeId): number {
}
export default class ContractRegistry {
public typeDefs: TypeDef[] = [];
public readonly typeDefs: TypeDef[] = [];
public readonly registry: Registry;
public project: InkProject;
public json: AnyJson;
constructor (json: AnyJson) {
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');
public readonly project: InkProject;
constructor (json: Record<string, AnyJson>) {
this.registry = new TypeRegistry();
this.json = json;
this.project = this.registry.createType('InkProject', json);
// Generate TypeDefs for each provided registry type
+2 -2
View File
@@ -35,9 +35,9 @@ export default class Blueprint<ApiType extends ApiTypes> extends Base<ApiType> {
this.codeHash = this.registry.createType('Hash', codeHash);
}
public createContract (constructorOrIndex: AbiConstructor | number, endowment: BigInt | string | number | BN, gasLimit: BigInt | string | number | BN, ...params: CodecArg[]): SubmittableExtrinsic<ApiType, BlueprintSubmittableResult<ApiType>> {
public createContract (constructorOrId: AbiConstructor | string| number, endowment: BigInt | string | number | BN, gasLimit: BigInt | string | number | BN, ...params: CodecArg[]): SubmittableExtrinsic<ApiType, BlueprintSubmittableResult<ApiType>> {
return this.api.tx.contracts
.instantiate(endowment, gasLimit, this.codeHash, encodeMessage(this.registry, this.abi.findConstructor(constructorOrIndex), params))
.instantiate(endowment, gasLimit, this.codeHash, encodeMessage(this.registry, this.abi.findConstructor(constructorOrId), params))
.withResultTransform((result: ISubmittableResult) =>
new BlueprintSubmittableResult(result, applyOnEvent(result, 'Instantiated', (record: EventRecord) =>
new Contract<ApiType>(this.api, this.abi, record.event.data[1] as AccountId, this._decorateMethod)
+1 -1
View File
@@ -28,7 +28,7 @@ export class CodeSubmittableResult<ApiType extends ApiTypes> extends Submittable
export default class Code<ApiType extends ApiTypes> extends Base<ApiType> {
public readonly code: Uint8Array;
constructor (api: ApiBase<ApiType>, abi: AnyJson | Abi, wasm: Uint8Array | string, decorateMethod: DecorateMethod<ApiType>) {
constructor (api: ApiBase<ApiType>, abi: AnyJson | Abi, wasm: Uint8Array | string | Buffer, decorateMethod: DecorateMethod<ApiType>) {
super(api, abi, decorateMethod);
this.code = u8aToU8a(wasm);
+5 -5
View File
@@ -47,18 +47,18 @@ export default class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
return isFunction(this.api.rx.rpc.contracts?.call);
}
public exec (messageOrIndex: AbiMessage | number, value: BigInt | BN | string | number, gasLimit: BigInt | BN | string | number, ...params: CodecArg[]): SubmittableExtrinsic<ApiType> {
return this.api.tx.contracts.call(this.address, value, gasLimit, encodeMessage(this.registry, this.abi.findMessage(messageOrIndex), params));
public exec (messageOrId: AbiMessage | string | number, value: BigInt | BN | string | number, gasLimit: BigInt | BN | string | number, ...params: CodecArg[]): SubmittableExtrinsic<ApiType> {
return this.api.tx.contracts.call(this.address, value, gasLimit, encodeMessage(this.registry, this.abi.findMessage(messageOrId), params));
}
public read (messageOrIndex: AbiMessage | number, value: BigInt | BN | string | number, gasLimit: BigInt | BN | string | number, ...params: CodecArg[]): ContractRead<ApiType> {
public read (messageOrId: AbiMessage | string | number, value: BigInt | BN | string | number, gasLimit: BigInt | BN | string | number, ...params: CodecArg[]): ContractRead<ApiType> {
assert(this.hasRpcContractsCall, 'Your node does not support contract RPC read calls');
const message = this.abi.findMessage(messageOrIndex);
const message = this.abi.findMessage(messageOrId);
return {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
send: this._decorateMethod((account: IKeyringPair | string | AccountId): ContractCallResult<'rpc'> =>
send: this._decorateMethod((account: string | AccountId | Uint8Array): ContractCallResult<'rpc'> =>
this.api.rx.rpc.contracts.call({
dest: this.address,
gasLimit,
@@ -9,7 +9,7 @@ import Abi from '../Abi';
import Code from '../base/Code';
export default class PromiseCode extends Code<'promise'> {
constructor (api: ApiPromise, abi: AnyJson | Abi, wasm: string | Uint8Array) {
constructor (api: ApiPromise, abi: AnyJson | Abi, wasm: Uint8Array | string | Buffer) {
super(api, abi, wasm, decorateMethod);
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
export { default as PromiseBlueprint } from './PromiseBlueprint';
export { default as PromiseCode } from './PromiseCode';
export { default as PromiseContract } from './PromiseContract';
export { default as BlueprintPromise } from './PromiseBlueprint';
export { default as CodePromise } from './PromiseCode';
export { default as ContractPromise } from './PromiseContract';
+1 -1
View File
@@ -10,7 +10,7 @@ import Abi from '../Abi';
import Code from '../base/Code';
export default class RxCode extends Code<'rxjs'> {
constructor (api: ApiRx, abi: AnyJson | Abi, wasm: string | Uint8Array) {
constructor (api: ApiRx, abi: AnyJson | Abi, wasm: Uint8Array | string | Buffer) {
super(api, abi, wasm, decorateMethod);
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
export { default as RxBlueprint } from './RxBlueprint';
export { default as RxCode } from './RxCode';
export { default as RxContract } from './RxContract';
export { default as BlueprintRx } from './RxBlueprint';
export { default as CodeRx } from './RxCode';
export { default as ContractRx } from './RxContract';