Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert api-contract usage of api.rpc.* to api.call.* #5107

Merged
merged 4 commits into from
Jul 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
2 changes: 1 addition & 1 deletion packages/api-contract/src/base/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export abstract class Base<ApiType extends ApiTypes> {

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');
Expand Down
90 changes: 41 additions & 49 deletions packages/api-contract/src/base/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
});
}

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<ApiType> {
if (!this.hasRpcContractsCall) {
if (!this.hasRpcContractsApi) {
throw new Error(ERROR_NO_CALL);
}

Expand All @@ -112,64 +112,56 @@ export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
};

#exec = (messageOrId: AbiMessage | string | number, { gasLimit = BN_ZERO, storageDepositLimit = null, value = BN_ZERO }: ContractOptions, params: unknown[]): SubmittableExtrinsic<ApiType> => {
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 null;
}
})
.filter((decoded): decoded is DecodedEvent => !!decoded)
))
);

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)
))
);
};

#read = (messageOrId: AbiMessage | string | number, { gasLimit = BN_ZERO, storageDepositLimit = null, value = BN_ZERO }: ContractOptions, params: unknown[]): ContractCallSend<ApiType> => {
if (!this.hasRpcContractsCall) {
if (!this.hasRpcContractsApi) {
throw new Error(ERROR_NO_CALL);
}

const message = this.abi.findMessage(messageOrId);

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<ContractExecResult>(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
}))
)
)
};
};
}
Expand Down
7 changes: 7 additions & 0 deletions packages/api-contract/src/base/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down