Skip to content
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
15 changes: 13 additions & 2 deletions docs/api-contract/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,24 @@ When passing an older pre ink! 3.0-rc1 version of the ABI, you will have an "Inv
If you are using an older version you would need to use an older version of the API or upgrade your contracts to ink! 3.0.


## After upgrading I don't have isSuccess/isError

In earlier versions of Substrate the call results via read had a slightly different interface to what it available now. Specifically on the `result` structure retrieved via read calls `isOk` was named `isSuccess` (and `isErr` was named `isError`). Since the `Contract` interface follows the Substrate convention these changes has been applied alongside the Substrate update to the `ContractExecResult` structure.

In addition `asErr` (unlike the older `asError`) now also has a full error enum (mapping to `DispatchError`) containing failures, unlike the older interface where this was not available. On older chains due to lack of information this will always be `Other`, while on newer chains the result will be fully populated.

The `Contract` interface, despite these underlying naming changes, transparently maps older responses (on older, not yet upgraded chains) to the newer structure, so while there is an change to the JS code use required to cater for this new structure, it can be used against both old and new chains with a transparent mapping between.


## Why is there a snake_case vs camelCase difference

The API always tries to use `camelCase` where available. This aligns with the de-facto standards that are generally (not always!) used in JS interfaces. This means that when decorating the ABIs into `contract.<query|tx>.methodName` the `methodName` part would be in camelCase format.
The API always tries to use `camelCase` naming where available. This aligns with the de-facto standards that are generally (not always!) used in JS interfaces. This means that when decorating the ABIs into `contract.<query|tx>.methodName` the `methodName` part would be in camelCase format.

An example of this would be in the erc20 ink! ABI - the method in the above would be `balance_of` however the API (for consistency with the full quite of libraries), decorate this as `contract.query.balanceOf`. When calling the `.read` or `.exec` directly on the contract, you should still specify the original ABI identifier, e.g. `contract.read('balance_of', ...)` (In the next release this will also allow for camelCase lookups in addition to the original Rust/Solidity naming)


## How do I subscribe to a contract query?

Subscriptions, and queries to the raw storage are on their way! Unfortunately until then there isn't a proper way to subscribe to a contract query. A temporary workaround is to subscribe to `api.query.contracts.contractInfoOf`.

```javascript
Expand All @@ -30,4 +41,4 @@ const unsub = await api.query.contracts.contractInfoOf(contractAddress, async ()
});
```

But this workaround is not without drawbacks. Since the callback will be executed every time the contract's storage is affected you will ultimately end up calling your contract query more often than necessary.
But this workaround is not without drawbacks. Since the callback will be executed every time the contract's storage is affected you will ultimately end up calling your contract query more often than necessary.
2 changes: 1 addition & 1 deletion docs/api-contract/start/blueprint.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ const unsub = await blueprint

## Interact with contracts

We have made it this far. At this point you should be familiar with code deployments as well as contract instantiation, next up [we will read a contract and send transactions to it](contract.md).
We have made it this far. At this point you should be familiar with code deployments as well as contract instantiation, next up [we will read a contract](contract.read.md).
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,20 @@ const gasLimit = 3000n * 1000000n;

// Perform the actual read (no params at the end, for the `get` message)
// (We perform the send from an account, here using Alice's address)
const callValue = await contract.query.get(alicePair.address, value, gasLimit);
const { gasConsumed, result, outcome } = await contract.query.get(alicePair.address, value, gasLimit);

// The actual result from RPC as `ContractExecResult`
console.log(callValue.result.toHuman());
console.log(result.toHuman());

// check if the call was successful
if (callValue.result.isSuccess) {
// data from the enum
const success = callValue.result.asSuccess;
// gas consumed
console.log(gasConsumed.toHuman());

// check if the call was successful
if (result.isOk) {
// should output 123 as per our initial set (output here is an i32)
console.log(callValue.output.toHuman());

// the amount of gas consumed (naturally a u64 value()
console.log(success.gasConsumed.toHuman());
console.log('Success', output.toHuman());
} else {
console.error('Call failed');
console.error('Error', result.asErr);
}
```

Expand Down Expand Up @@ -84,69 +81,5 @@ In cases where the ABI messages have conflicting names, instead of the `'get'` s

## Sending a transaction

In addition to using the `.query.<messageName>` on a contract, the `.tx.<messageName>` method is provides to send an actual encoded transaction to the contract. Expanding on our above example, we can now execute and then retrieve the subsequent value -

```javascript
// We will use these values for the execution
const value = 0; // only useful on isPayable messages
const gasLimit = 3000n * 1000000n;
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 paras, if required can follow - here only one is needed)
await contract.tx
.inc(value, gasLimit, incValue)
.signAndSend(alicePair, (result) => {
if (result.status.isInBlock) {
console.log('in a block');
} else if (result.status.isFinalized) {
console.log('finalized');
}
});
```

If we perform the same `query.get` read on the value now, it would be `124`. For lower-level access, like we have in the `Blueprint` via `.createContract` you can also perform the execution via the `.exec` function, which would yield equivalent results -

```javascript
// Send the transaction, like elsewhere this is a normal submittable
// extrinsic with the same rules as applied in the API
await contract
.exec('inc', value, gasLimit, incValue)
.signAndSend(alicePair, (result) => {
...
});
```

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


## Weight estimation

To estimate the gasLimit (which in the Substrate context refers to the weight used), we can use the `.query` (read) interfaces with a sufficiently large value to retrieve the actual gas consumed. The API makes this easy - with a `gasLimit` or `-1` passed to the query it will use the maximum gas limit available to transactions and the return value will have the actual gas used.

To see this in practice -

```js
// We will use these values for the execution
const value = 0;
const incValue = 1;

// Instead of sending we use the `call` interface via `.query` that will return
// the gas consoumed (the API aut-fill the max block tx weight when -1 is the gasLimit)
const { result } = await contract.query.inc(value, -1, incValue)

if (result.isSuccess) {
// extract the value from the Success portion of the enum
const gasConsumed = result.asSuccess.gasConsumed;

console.log(`Call execution will consume ${gasConsumed.toString()}`);
}
```

We can use the `gasConsumed` 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.


## That is it... for now
Now that we understand the underlying call/read interfaces where a message is executed, but not part of a block, we will loo into [sending transaction messages](contract.tx.md) in our next section.

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.
73 changes: 73 additions & 0 deletions docs/api-contract/start/contract.tx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: Contract Tx
---

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

```javascript
// We will use these values for the execution
const value = 0; // only useful on isPayable messages
const gasLimit = 3000n * 1000000n;
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(value, gasLimit, incValue)
.signAndSend(alicePair, (result) => {
if (result.status.isInBlock) {
console.log('in a block');
} else if (result.status.isFinalized) {
console.log('finalized');
}
});
```

If we perform the same `query.get` read on the value now, it would be `124`. For lower-level access, like we have in the `Blueprint` via `.createContract` you can also perform the execution via the `.exec` function, which would yield equivalent results -

```javascript
// Send the transaction, like elsewhere this is a normal submittable
// extrinsic with the same rules as applied in the API
await contract
.exec('inc', value, gasLimit, incValue)
.signAndSend(alicePair, (result) => {
...
});
```

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


## Weight estimation

To estimate the gasLimit (which in the Substrate context refers to the weight used), we can use the `.query` (read) interfaces with a sufficiently large value to retrieve the actual gas consumed. The API makes this easy - with a `gasLimit` or `-1` passed to the query it will use the maximum gas limit available to transactions and the return value will have the actual gas used.

To see this in practice -

```js
// We will use these values for the execution
const value = 0;
const incValue = 1;

// Instead of sending we use the `call` interface via `.query` that will return
// the gas consumed (the API aut-fill the max block tx weight when -1 is the gasLimit)
const { gasConsumed, result } = await contract.query.inc(value, -1, incValue)

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

We can use the `gasConsumed` 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.
3 changes: 2 additions & 1 deletion sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ module.exports = {
'api-contract/start/basics',
'api-contract/start/code',
'api-contract/start/blueprint',
'api-contract/start/contract'
'api-contract/start/contract.read',
'api-contract/start/contract.tx'
]
},
'api-contract/FAQ'
Expand Down