Skip to main content

Contract tx

Interface

In addition to using the .query.<messageName> on a contract, the .tx.<messageName> method provides a way to send an actual encoded transaction to the contract, allow for execution and have this applied in a block. Expanding on our previous ink! incrementer example, we can now execute and then retrieve the subsequent value.

const value = 10000; // only for payable messages, call will fail otherwise
const gasLimit = 3000n * 1000000n;
const storageDepositLimit = null;
const incValue = 1;

// Send the transaction, like elsewhere this is a normal extrinsic
// with the same rules as applied in the API (As with the read example,
// additional params, if required can follow - here only one is needed)
await contract.tx
.inc({ storageDepositLimit, gasLimit }, incValue)
.signAndSend(alicePair, result => {
if (result.status.isInBlock) {
console.log('in a block');
} else if (result.status.isFinalized) {
console.log('finalized');
}
});

For the above interface we can specify the message as the string name, the index of the actual message as retrieved via the Abi.

Cost estimation

To estimate values for gasLimit and storageDepositLimit, we can dry run the contract call using the .query (read) interfaces with a sufficiently large value to retrieve the actual gas and storage deposit consumed. The API makes this easy by passing gasLimit: -1 and storageDepositLimit: null to the query. The query will use the maximum tx weight for gasLimit and available free balance for storageDepositLimit.

See this in practice for the inc message on the ink! incrementer contract

const incValue = 1;
const options = { storageDepositLimit: null, gasLimit: -1 }

const { gasRequired, storageDeposit, result } = await contract.query.inc(
alicePair,
options,
incValue
);

console.log(`outcome: ${result.isOk ? 'Ok' : 'Error'}`);
console.log(`gasRequired ${gasRequired.toString()}`);

We can use the gasRequired input (potentially with a buffer for various execution paths) in any calls to contract.tx.inc(...) with the same input parameters specified on the query where the estimation was done.

Events

On current versions of the API, any events raised by the contract will be transparently decoded with the relevant ABI and will be made available on the result (from .signAndSend(alicePair, (result) => {...}) as contractEvents.

Where no events were emitted this value would be undefined, however should events be emitted, the array will contain all the decoded values.

That is it... for now

This was a whirl-wind tour of what the API provides in terms of the @polkadot/api-contract interface. It is not perfect yet, we would like to expand it to allow for greater type-checking on the contracts (instead of read/exec wit messages), but hopefully in the current state it already enhances the way you can interact with contracts.