Updated ABI definitions & parsing (#1662)
* Updated ABI definitions & parsing * Run type generation * Definitions (should be ok-ish) * Cleanup names * Allow aliasses on structs (JSON parsing) * Cleanup json mapping passing * s/types/lookup/ * Parsing as per current output * InkSelector * MtRegistryIndex * String lookup tests * Remove serd ovverride comments (it works as-is) * inkRegistry tests * Extract primitive types * s/ty/id/ * Start of parsing into types (actually to be TypeDef) * Parsing * stringify struct fields * Small cleanups * Add type index to project lookup * Prefix with index * Fix interface generation * Latest contract metadata (#2118) * Add new (provisional) incrementer contract metadata * Update and regenerate type definitions * WIP: generate new format scale-info types * WIP: implementing variants * Implement variants * Fix up type resolution * Fix registry spec tests * Fix lints and some tests * Fix inkTypes tests * Fix some lints * Make imports same line like original * Comment out (temporarily) sanitize import to make CI pass * Sanitize * Remove indices * Remove index * Adjust ContractAbi interface (empty rpc) * linting * Pull in Andrew's changes * Cleanups * Some more cleanups * Update with v3 contracts * Adjust with actual types * Regenerate * Update ABI parsing (#2695) * Adjust ABI code * Cleanup * Merge non-code * Type naming * Simplify Base * Rename 1 * rename 2 * Rename 3 * Simplify * Add index to AbiMessage * Pass actual message or index for call * Rename param * Simplify * Expose call & exec * s/call/read/ * Cleanup asserts * AccountId as per spec * displayName is path * Update definitions * Flatten ABIType into TypeDef * Rewrite * Fix tests * Updates Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
co-authored by
Andrew Jones
parent
89e0548092
commit
453dd9e49a
@@ -0,0 +1,34 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ApiTypes, DecorateMethod, SubmittableModuleExtrinsics } from '@polkadot/api/types';
|
||||
import { AnyJson, Registry } from '@polkadot/types/types';
|
||||
import { ApiObject } from '../types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
import Abi from '../Abi';
|
||||
|
||||
export default abstract class Base<ApiType extends ApiTypes> {
|
||||
public readonly abi: Abi;
|
||||
|
||||
public readonly api: ApiObject<ApiType>;
|
||||
|
||||
public readonly registry: Registry;
|
||||
|
||||
protected readonly _decorateMethod: DecorateMethod<ApiType>;
|
||||
|
||||
constructor (api: ApiObject<ApiType>, abi: AnyJson | Abi, decorateMethod: DecorateMethod<ApiType>) {
|
||||
this.abi = abi instanceof Abi
|
||||
? abi
|
||||
: new Abi(api.registry, abi);
|
||||
this.api = api;
|
||||
this.registry = api.registry;
|
||||
this._decorateMethod = decorateMethod;
|
||||
|
||||
assert(this.api.rx.tx.contracts && this.api.rx.tx.contracts.putCode, 'You need to connect to a node with the contracts module, the metadata does not enable api.tx.contracts on this instance');
|
||||
}
|
||||
|
||||
protected get _apiContracts (): SubmittableModuleExtrinsics<'rxjs'> {
|
||||
return this.api.rx.tx.contracts;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ApiTypes, DecorateMethod } from '@polkadot/api/types';
|
||||
import { AccountId, Address, Hash } from '@polkadot/types/interfaces';
|
||||
import { IKeyringPair, ISubmittableResult } from '@polkadot/types/types';
|
||||
import { ApiObject, ContractABIPre } from '../types';
|
||||
import { AccountId, EventRecord, Hash } from '@polkadot/types/interfaces';
|
||||
import { AnyJson, IKeyringPair, ISubmittableResult } from '@polkadot/types/types';
|
||||
import { ApiObject } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -13,14 +13,15 @@ import { SubmittableResult } from '@polkadot/api';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import Base from './Base';
|
||||
import Contract from './Contract';
|
||||
import { BaseWithTx } from './util';
|
||||
import { applyOnEvent } from './util';
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type BlueprintCreateResultSubscription<ApiType extends ApiTypes> = Observable<BlueprintCreateResult<ApiType>>;
|
||||
|
||||
export interface BlueprintCreate<ApiType extends ApiTypes> {
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address): BlueprintCreateResultSubscription<ApiType>;
|
||||
signAndSend (account: IKeyringPair | string | AccountId): BlueprintCreateResultSubscription<ApiType>;
|
||||
}
|
||||
|
||||
class BlueprintCreateResult<ApiType extends ApiTypes> extends SubmittableResult {
|
||||
@@ -34,10 +35,10 @@ class BlueprintCreateResult<ApiType extends ApiTypes> extends SubmittableResult
|
||||
}
|
||||
|
||||
// NOTE Experimental, POC, bound to change
|
||||
export default class Blueprint<ApiType extends ApiTypes> extends BaseWithTx<ApiType> {
|
||||
export default class Blueprint<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
public readonly codeHash: Hash;
|
||||
|
||||
constructor (api: ApiObject<ApiType>, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod<ApiType>, codeHash: string | Hash) {
|
||||
constructor (api: ApiObject<ApiType>, abi: AnyJson | Abi, decorateMethod: DecorateMethod<ApiType>, codeHash: string | Hash) {
|
||||
super(api, abi, decorateMethod);
|
||||
|
||||
this.codeHash = this.registry.createType('Hash', codeHash);
|
||||
@@ -48,28 +49,19 @@ export default class Blueprint<ApiType extends ApiTypes> extends BaseWithTx<ApiT
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
signAndSend: this.decorateMethod(
|
||||
(account: IKeyringPair | string | AccountId | Address): BlueprintCreateResultSubscription<ApiType> => {
|
||||
return this._apiContracts
|
||||
signAndSend: this._decorateMethod(
|
||||
(account: IKeyringPair | string | AccountId): BlueprintCreateResultSubscription<ApiType> =>
|
||||
this._apiContracts
|
||||
.create(endowment, maxGas, this.codeHash, this.abi.constructors[constructorIndex](...params))
|
||||
.signAndSend(account)
|
||||
.pipe(map(this._createResult));
|
||||
}
|
||||
.pipe(
|
||||
map((result) =>
|
||||
new BlueprintCreateResult(result, applyOnEvent(result, 'Instantiated', (record: EventRecord) =>
|
||||
new Contract<ApiType>(this.api, this.abi, this._decorateMethod, record.event.data[1] as AccountId)
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private _createResult = (result: SubmittableResult): BlueprintCreateResult<ApiType> => {
|
||||
let contract: Contract<ApiType> | undefined;
|
||||
|
||||
if (result.isInBlock) {
|
||||
const record = result.findRecord('contract', 'Instantiated');
|
||||
|
||||
if (record) {
|
||||
contract = new Contract<ApiType>(this.api, this.abi, this.decorateMethod, record.event.data[1] as Address);
|
||||
}
|
||||
}
|
||||
|
||||
return new BlueprintCreateResult(result, contract);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ApiTypes, DecorateMethod } from '@polkadot/api/types';
|
||||
import { AccountId, Address, Hash } from '@polkadot/types/interfaces';
|
||||
import { IKeyringPair, ISubmittableResult } from '@polkadot/types/types';
|
||||
import { ApiObject, ContractABIPre } from '../types';
|
||||
import { AccountId, Address, EventRecord, Hash } from '@polkadot/types/interfaces';
|
||||
import { AnyJson, IKeyringPair, ISubmittableResult } from '@polkadot/types/types';
|
||||
import { ApiObject } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -13,8 +13,9 @@ import { SubmittableResult } from '@polkadot/api';
|
||||
import { compactAddLength, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import Base from './Base';
|
||||
import Blueprint from './Blueprint';
|
||||
import { BaseWithTx } from './util';
|
||||
import { applyOnEvent } from './util';
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type CodePutCodeResultSubscription<ApiType extends ApiTypes> = Observable<CodePutCodeResult<ApiType>>;
|
||||
@@ -34,10 +35,10 @@ class CodePutCodeResult<ApiType extends ApiTypes> extends SubmittableResult {
|
||||
}
|
||||
|
||||
// NOTE Experimental, POC, bound to change
|
||||
export default class Code<ApiType extends ApiTypes> extends BaseWithTx<ApiType> {
|
||||
export default class Code<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
public readonly code: Uint8Array;
|
||||
|
||||
constructor (api: ApiObject<ApiType>, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod<ApiType>, wasm: string | Uint8Array) {
|
||||
constructor (api: ApiObject<ApiType>, abi: AnyJson | Abi, decorateMethod: DecorateMethod<ApiType>, wasm: Uint8Array | string) {
|
||||
super(api, abi, decorateMethod);
|
||||
|
||||
this.code = u8aToU8a(wasm);
|
||||
@@ -46,27 +47,19 @@ export default class Code<ApiType extends ApiTypes> extends BaseWithTx<ApiType>
|
||||
public createBlueprint = (maxGas: number | BN): CodePutCode<ApiType> => {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
signAndSend: this.decorateMethod(
|
||||
(account: IKeyringPair | string | AccountId | Address): CodePutCodeResultSubscription<ApiType> =>
|
||||
signAndSend: this._decorateMethod(
|
||||
(account: IKeyringPair | string | AccountId): CodePutCodeResultSubscription<ApiType> =>
|
||||
this._apiContracts
|
||||
.putCode(maxGas, compactAddLength(this.code))
|
||||
.signAndSend(account)
|
||||
.pipe(map(this._createResult))
|
||||
.pipe(
|
||||
map((result) =>
|
||||
new CodePutCodeResult(result, applyOnEvent(result, 'CodeStored', (record: EventRecord) =>
|
||||
new Blueprint<ApiType>(this.api, this.abi, this._decorateMethod, record.event.data[0] as Hash)
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private _createResult = (result: ISubmittableResult): CodePutCodeResult<ApiType> => {
|
||||
let blueprint: Blueprint<ApiType> | undefined;
|
||||
|
||||
if (result.isInBlock) {
|
||||
const record = result.findRecord('contract', 'CodeStored');
|
||||
|
||||
if (record) {
|
||||
blueprint = new Blueprint<ApiType>(this.api, this.abi, this.decorateMethod, record.event.data[0] as Hash);
|
||||
}
|
||||
}
|
||||
|
||||
return new CodePutCodeResult(result, blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,20 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ApiTypes, DecorateMethod, ObsInnerType } from '@polkadot/api/types';
|
||||
import { AccountId, Address, ContractExecResult } from '@polkadot/types/interfaces';
|
||||
import { Codec, CodecArg, IKeyringPair } from '@polkadot/types/types';
|
||||
import { ApiObject, ContractABIMessage, ContractABIPre, ContractCallOutcome } from '../types';
|
||||
import { AccountId, ContractExecResult } from '@polkadot/types/interfaces';
|
||||
import { AnyJson, Codec, CodecArg, IKeyringPair } from '@polkadot/types/types';
|
||||
import { ApiObject, AbiMessage, ContractCallOutcome } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { SubmittableResult } from '@polkadot/api';
|
||||
import { assert, isFunction, isNumber } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import { formatData } from '../util';
|
||||
import { BaseWithTxAndRpcCall } from './util';
|
||||
import Base from './Base';
|
||||
import { SignerOptions } from '@polkadot/api/submittable/types';
|
||||
|
||||
type ContractCallTypes = 'tx' | 'rpc';
|
||||
|
||||
@@ -22,71 +25,95 @@ type ContractCallResultSubscription<ApiType extends ApiTypes, CallType extends C
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
: Promise<ObsInnerType<ContractCallResult<CallType>>>;
|
||||
|
||||
export interface ContractCall<ApiType extends ApiTypes, CallType extends ContractCallTypes> {
|
||||
send (account: IKeyringPair | string | AccountId | Address): ContractCallResultSubscription<ApiType, CallType>;
|
||||
export interface ContractExec<ApiType extends ApiTypes> {
|
||||
signAndSend (account: IKeyringPair | string | AccountId, options?: Partial<SignerOptions>): ContractCallResultSubscription<ApiType, 'tx'>;
|
||||
}
|
||||
|
||||
export interface ContractRead<ApiType extends ApiTypes> {
|
||||
send (account: IKeyringPair | string | AccountId): ContractCallResultSubscription<ApiType, 'rpc'>;
|
||||
}
|
||||
|
||||
export type ContractCallResult<CallType extends ContractCallTypes> = CallType extends 'rpc'
|
||||
? Observable<ContractCallOutcome>
|
||||
: Observable<SubmittableResult>;
|
||||
|
||||
export default class Contract<ApiType extends ApiTypes> extends BaseWithTxAndRpcCall<ApiType> {
|
||||
public readonly address: Address;
|
||||
export default class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
public readonly address: AccountId;
|
||||
|
||||
constructor (api: ApiObject<ApiType>, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod<ApiType>, address: string | AccountId | Address) {
|
||||
constructor (api: ApiObject<ApiType>, abi: AnyJson | Abi, decorateMethod: DecorateMethod<ApiType>, address: string | AccountId) {
|
||||
super(api, abi, decorateMethod);
|
||||
|
||||
this.address = this.registry.createType('Address', address);
|
||||
this.address = this.registry.createType('AccountId', address);
|
||||
}
|
||||
|
||||
public call (as: 'rpc', message: string, value: BN | number, gasLimit: BN | number, ...params: CodecArg[]): ContractCall<ApiType, 'rpc'>;
|
||||
public call (as: 'tx', message: string, value: BN | number, gasLimit: BN | number, ...params: CodecArg[]): ContractCall<ApiType, 'tx'>;
|
||||
public call<CallType extends ContractCallTypes> (as: CallType, message: string, value: BN | number, gasLimit: BN | number, ...params: CodecArg[]): ContractCall<ApiType, CallType> {
|
||||
const { def, fn } = this.getMessage(message);
|
||||
public get hasRpcContractsCall (): boolean {
|
||||
return isFunction(this.api.rx.rpc.contracts?.call);
|
||||
}
|
||||
|
||||
private _createInput (message: AbiMessage | number, params: CodecArg[]): [Uint8Array, AbiMessage] {
|
||||
const fn = isNumber(message)
|
||||
? this.abi.messages[message]
|
||||
: message;
|
||||
|
||||
assert(fn, 'Attempted to call an invalid contract message');
|
||||
|
||||
return [fn(...params), fn];
|
||||
}
|
||||
|
||||
public exec = (message: AbiMessage | number, value: BN | string | number, gasLimit: BN | string | number, ...params: CodecArg[]): ContractExec<ApiType> => {
|
||||
const [inputData] = this._createInput(message, params);
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
send: this.decorateMethod(
|
||||
as === 'rpc' && this.hasRpcContractsCall
|
||||
? (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'rpc'> =>
|
||||
this._rpcContractsCall(
|
||||
this.registry.createType('ContractCallRequest', {
|
||||
dest: this.address.toString(),
|
||||
gasLimit,
|
||||
inputData: fn(...params),
|
||||
origin: account,
|
||||
value
|
||||
})
|
||||
).pipe(map((result: ContractExecResult): ContractCallOutcome =>
|
||||
this._createOutcome(result, this.registry.createType('AccountId', account), def, params)
|
||||
))
|
||||
: (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'tx'> =>
|
||||
this._apiContracts
|
||||
.call(this.address, value, gasLimit, fn(...params))
|
||||
.signAndSend(account)
|
||||
signAndSend: this._decorateMethod((account: IKeyringPair | string | AccountId, options?: Partial<SignerOptions>): ContractCallResult<'tx'> =>
|
||||
this._apiContracts
|
||||
.call(this.address, value, gasLimit, inputData)
|
||||
.signAndSend(account, options)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private _createOutcome (result: ContractExecResult, origin: AccountId, message: ContractABIMessage, params: CodecArg[]): ContractCallOutcome {
|
||||
let output: Codec | null = null;
|
||||
public read = (message: AbiMessage | number, value: BN | string | number, gasLimit: BN | string | number, ...params: CodecArg[]): ContractRead<ApiType> => {
|
||||
assert(this.hasRpcContractsCall, 'Your node does not support contract RPC read calls');
|
||||
|
||||
if (result.isSuccess) {
|
||||
const { data } = result.asSuccess;
|
||||
|
||||
output = message.returnType
|
||||
? formatData(this.registry, data, message.returnType)
|
||||
: this.registry.createType('Raw', data);
|
||||
}
|
||||
const [inputData, messageFn] = this._createInput(message, params);
|
||||
|
||||
return {
|
||||
isSuccess: result.isSuccess,
|
||||
message,
|
||||
origin,
|
||||
output,
|
||||
params,
|
||||
result,
|
||||
time: Date.now()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
send: this._decorateMethod((account: IKeyringPair | string | AccountId): ContractCallResult<'rpc'> =>
|
||||
this.api.rx.rpc.contracts.call(
|
||||
this.registry.createType('ContractCallRequest', {
|
||||
dest: this.address,
|
||||
gasLimit,
|
||||
inputData,
|
||||
origin: account,
|
||||
value
|
||||
})
|
||||
).pipe(
|
||||
map((result: ContractExecResult): ContractCallOutcome => {
|
||||
let output: Codec | null = null;
|
||||
|
||||
if (result.isSuccess) {
|
||||
const { data } = result.asSuccess;
|
||||
|
||||
output = messageFn.returnType
|
||||
? formatData(this.registry, data, messageFn.returnType)
|
||||
: this.registry.createType('Raw', data);
|
||||
}
|
||||
|
||||
return {
|
||||
isSuccess: result.isSuccess,
|
||||
message: messageFn,
|
||||
origin: this.registry.createType('AccountId', account),
|
||||
output,
|
||||
params,
|
||||
result,
|
||||
time: Date.now(),
|
||||
type: messageFn.returnType || null
|
||||
};
|
||||
})
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +1,17 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ApiTypes, DecorateMethod, DecoratedRpc, SubmittableModuleExtrinsics } from '@polkadot/api/types';
|
||||
import { RpcInterface } from '@polkadot/rpc-core/types';
|
||||
import { Registry } from '@polkadot/types/types';
|
||||
import { ApiObject, ContractABIMessage, ContractABIPre, ContractBase, ContractMessage } from '../types';
|
||||
import { SubmittableResult } from '@polkadot/api';
|
||||
import { EventRecord } from '@polkadot/types/interfaces';
|
||||
|
||||
import { assert, isFunction, stringCamelCase } from '@polkadot/util';
|
||||
import Abi from '../Abi';
|
||||
export function applyOnEvent <T> (result: SubmittableResult, type: 'CodeStored' | 'Instantiated', fn: (record: EventRecord) => T): T | undefined {
|
||||
if (result.isInBlock || result.isFinalized) {
|
||||
const record = result.findRecord('contract', 'CodeStored');
|
||||
|
||||
export abstract class Base<ApiType extends ApiTypes> implements ContractBase<ApiType> {
|
||||
public readonly abi: Abi;
|
||||
|
||||
public readonly api: ApiObject<ApiType>;
|
||||
|
||||
public readonly decorateMethod: DecorateMethod<ApiType>;
|
||||
|
||||
public readonly registry: Registry;
|
||||
|
||||
constructor (api: ApiObject<ApiType>, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod<ApiType>) {
|
||||
this.abi = abi instanceof Abi
|
||||
? abi
|
||||
: new Abi(api.registry, abi);
|
||||
this.registry = api.registry;
|
||||
this.api = api;
|
||||
this.decorateMethod = decorateMethod;
|
||||
if (record) {
|
||||
return fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
public get messages (): ContractMessage[] {
|
||||
return this.abi.abi.contract.messages.map(
|
||||
(def: ContractABIMessage, index): ContractMessage => ({
|
||||
def,
|
||||
fn: this.abi.messages[def.name] || this.abi.messages[stringCamelCase(name)],
|
||||
index
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public getMessage (nameOrIndex?: string | number): ContractMessage {
|
||||
const index = nameOrIndex
|
||||
? typeof nameOrIndex === 'number'
|
||||
? nameOrIndex
|
||||
: this.abi.abi.contract.messages.findIndex((message): boolean => nameOrIndex === message.name || nameOrIndex === stringCamelCase(message.name))
|
||||
: 0;
|
||||
const def = this.abi.abi.contract.messages[index];
|
||||
|
||||
assert(!!def, `Attempted to access a contract message that does not exist: ${typeof nameOrIndex === 'number' ? `index ${nameOrIndex}` : (nameOrIndex || 'unknown')}`);
|
||||
|
||||
const fn = this.abi.messages[def.name] || this.abi.messages[stringCamelCase(def.name)];
|
||||
|
||||
return { def, fn, index };
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseWithTx<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
protected get _apiContracts (): SubmittableModuleExtrinsics<'rxjs'> {
|
||||
return this.api.rx.tx.contracts;
|
||||
}
|
||||
|
||||
constructor (api: ApiObject<ApiType>, abi: ContractABIPre | Abi, decorateMethod: DecorateMethod<ApiType>) {
|
||||
super(api, abi, decorateMethod);
|
||||
|
||||
assert(this.api.rx.tx.contracts && this.api.rx.tx.contracts.putCode, 'You need to connect to a node with the contracts module, the metadata does not enable api.tx.contracts on this instance');
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseWithTxAndRpcCall<ApiType extends ApiTypes> extends BaseWithTx<ApiType> {
|
||||
public get hasRpcContractsCall (): boolean {
|
||||
return isFunction(this.api.rx.rpc.contracts?.call);
|
||||
}
|
||||
|
||||
protected get _rpcContractsCall (): DecoratedRpc<'rxjs', RpcInterface>['contracts']['call'] {
|
||||
assert(this.hasRpcContractsCall, 'You need to connect to a node with the contracts.call RPC method.');
|
||||
|
||||
return this.api.rx.rpc.contracts.call;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user