diff --git a/CHANGELOG.md b/CHANGELOG.md index 2873a8c738..08b1d98617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Changes: +- Convert `api-contract` usage of `api.rpc.*` to `api.call.*` +- Drop support for contract runtimes without `storageDepositLimit` (runtime `contractsApi` only has unversioned support for latest) - Export `api.rx.call.*` for internal usage (derive, contracts) diff --git a/packages/api-contract/src/base/Base.ts b/packages/api-contract/src/base/Base.ts index 52e3f4fd9f..ab36ca9ed9 100644 --- a/packages/api-contract/src/base/Base.ts +++ b/packages/api-contract/src/base/Base.ts @@ -26,7 +26,7 @@ export abstract class Base { if (!api || !api.isConnected || !api.tx) { throw new Error('Your API has not been initialized correctly and is not connected to a chain'); - } else if (!api.tx.contracts || !Object.keys(api.tx.contracts).length) { + } else if (!api.tx.contracts || !Object.keys(api.tx.contracts).length || !api.call.contractsApi) { throw new Error('You need to connect to a chain with a runtime that supports contracts'); } else if (!isFunction(api.tx.contracts.instantiateWithCode)) { throw new Error('You need to connect to a chain with a runtime with a V3 contracts module. The runtime does not expose api.tx.contracts.instantiateWithCode'); diff --git a/packages/api-contract/src/base/Contract.ts b/packages/api-contract/src/base/Contract.ts index dc3fd16e18..aa90223fff 100644 --- a/packages/api-contract/src/base/Contract.ts +++ b/packages/api-contract/src/base/Contract.ts @@ -82,12 +82,12 @@ export class Contract extends Base { }); } - public get hasRpcContractsCall (): boolean { - return isFunction(this.api.rx.rpc.contracts?.call); + public get hasRpcContractsApi (): boolean { + return isFunction(this.api.rx.call.contractsApi?.call); } public get query (): MapMessageQuery { - if (!this.hasRpcContractsCall) { + if (!this.hasRpcContractsApi) { throw new Error(ERROR_NO_CALL); } @@ -112,35 +112,31 @@ export class Contract extends Base { }; #exec = (messageOrId: AbiMessage | string | number, { gasLimit = BN_ZERO, storageDepositLimit = null, value = BN_ZERO }: ContractOptions, params: unknown[]): SubmittableExtrinsic => { - const hasStorageDeposit = this.api.tx.contracts.call.meta.args.length === 5; const gas = this.#getGas(gasLimit); const encParams = this.abi.findMessage(messageOrId).toU8a(params); - const tx = hasStorageDeposit - ? this.api.tx.contracts.call(this.address, value, gas, storageDepositLimit, encParams) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore old style without storage deposit - : this.api.tx.contracts.call(this.address, value, gas, encParams); - return tx.withResultTransform((result: ISubmittableResult) => - // ContractEmitted is the current generation, ContractExecution is the previous generation - new ContractSubmittableResult(result, applyOnEvent(result, ['ContractEmitted', 'ContractExecution'], (records: EventRecord[]) => - records - .map(({ event: { data: [, data] } }): DecodedEvent | null => { - try { - return this.abi.decodeEvent(data as Bytes); - } catch (error) { - l.error(`Unable to decode contract event: ${(error as Error).message}`); + return this.api.tx.contracts + .call(this.address, value, gas, storageDepositLimit, encParams) + .withResultTransform((result: ISubmittableResult) => + // ContractEmitted is the current generation, ContractExecution is the previous generation + new ContractSubmittableResult(result, applyOnEvent(result, ['ContractEmitted', 'ContractExecution'], (records: EventRecord[]) => + records + .map(({ event: { data: [, data] } }): DecodedEvent | null => { + try { + return this.abi.decodeEvent(data as Bytes); + } catch (error) { + l.error(`Unable to decode contract event: ${(error as Error).message}`); - return null; - } - }) - .filter((decoded): decoded is DecodedEvent => !!decoded) - )) - ); + return null; + } + }) + .filter((decoded): decoded is DecodedEvent => !!decoded) + )) + ); }; #read = (messageOrId: AbiMessage | string | number, { gasLimit = BN_ZERO, storageDepositLimit = null, value = BN_ZERO }: ContractOptions, params: unknown[]): ContractCallSend => { - if (!this.hasRpcContractsCall) { + if (!this.hasRpcContractsApi) { throw new Error(ERROR_NO_CALL); } @@ -148,28 +144,24 @@ export class Contract extends Base { return { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - send: this._decorateMethod((origin: string | AccountId | Uint8Array) => { - const hasStorageDeposit = this.api.tx.contracts.call.meta.args.length === 5; - const inputData = message.toU8a(params); - const rpc = hasStorageDeposit - ? this.api.rx.rpc.contracts.call({ dest: this.address, gasLimit: this.#getGas(gasLimit, true), inputData, origin, storageDepositLimit, value }) - : this.api.rx.rpc.contracts.call({ dest: this.address, gasLimit: this.#getGas(gasLimit, true), inputData, origin, value }); - - const mapFn = ({ debugMessage, gasConsumed, gasRequired, result, storageDeposit }: ContractExecResult): ContractCallOutcome => ({ - debugMessage, - gasConsumed, - gasRequired: gasRequired && !gasRequired.isZero() - ? gasRequired - : gasConsumed, - output: result.isOk && message.returnType - ? this.abi.registry.createTypeUnsafe(message.returnType.lookupName || message.returnType.type, [result.asOk.data.toU8a(true)], { isPedantic: true }) - : null, - result, - storageDeposit - }); - - return rpc.pipe(map(mapFn)); - }) + send: this._decorateMethod((origin: string | AccountId | Uint8Array) => + this.api.rx.call.contractsApi + .call(origin, this.address, value, this.#getGas(gasLimit, true), storageDepositLimit, message.toU8a(params)) + .pipe( + map(({ debugMessage, gasConsumed, gasRequired, result, storageDeposit }): ContractCallOutcome => ({ + debugMessage, + gasConsumed, + gasRequired: gasRequired && !gasRequired.isZero() + ? gasRequired + : gasConsumed, + output: result.isOk && message.returnType + ? this.abi.registry.createTypeUnsafe(message.returnType.lookupName || message.returnType.type, [result.asOk.data.toU8a(true)], { isPedantic: true }) + : null, + result, + storageDeposit + })) + ) + ) }; }; } diff --git a/packages/api-contract/src/base/mock.ts b/packages/api-contract/src/base/mock.ts index 455c0f32eb..f7151b5c21 100644 --- a/packages/api-contract/src/base/mock.ts +++ b/packages/api-contract/src/base/mock.ts @@ -7,6 +7,13 @@ import { TypeRegistry } from '@polkadot/types'; const registry = new TypeRegistry(); export const mockApi = { + call: { + contractsApi: { + call: (): never => { + throw new Error('mock'); + } + } + }, isConnected: true, registry, tx: {