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:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user