The example below shows how to subscribe to new blocks. It displays the block number every time a new block is seen by the node you are connected to.
A block hash refers to the hash over the header, the extrinsic hash refers to the hash of the encoded extrinsic. Since all objects returned by the API implement the .hash => Hash
getter, we can simply use this to view the actual hash.
The block author is encoded inside the consensus logs for the block. To extract it, you need to decode the log (which the API does do) and then map the index of the validator to the list of session validators. This extraction is however available on the API derive for new head subscriptions, which returns an extended header with the author populated (assuming that the digest logs are known).
For a single header only, the derives also contain a getHeader
, which once again returns a header extended with the author:
The transactions are included in a signed block as part of the extrinsics - some of these will be unsigned and generated by the block author and some of these may be submitted from external sources and be signed (some palettes use unsigned transactions, so signed/unsigned is not an indication of origin). To retrieve the block and display the transaction information, we can do the following:
In the above example, .toHuman()
is used to format the extrinsic into a human-readable representation. You can inspect/extract specific fields from the decoded extrinsic as required. For instance ex.method.section
would return the pallet that executed this transaction.
While the blocks contain the extrinsics, the system event storage will include the events and the details needed to allow for a mapping between them. For events, the phase
is an enum that would be isApplyExtrinsic
with the index in the cases where it refers to an extrinsic in a block. This index maps through the order of the extrinsics as found.
To perform a mapping between the two, we need information from from both sources, as per the example below.
The code below extends the above example, where extrinsics are mapped to their blocks. In this example, we will specifically look for specific extrinsic events—namely, the system.ExtrinsicSuccess
and system.ExtrinsicFailed
events. The same logic can be applied to inspect any other type of expected event.
Query the system events and extract information from them.
In the metadata, a fallback is provided for each storage item. This means that when an entry does not exist, the fallback (which is the default value for the type) will be provided - querying for a non-existent key (unless an option) will yield a value:
In the second case, the non-existent prefs returns the default/fallback value for the storage item. So in this case, we don't know if the value is set to 0 or unset. Existence can be checked by using the storage size, which would be zero if nothing is stored.
.entries()/.keys()
on double maps?As explained in the section Building with Native API each map-type storage entry exposes the entries/keys helpers to retrieve the whole list. In the case of double maps, with the addition of a single argument, you can retrieve either all entries or a subset based on the first map key.
In both these cases, entries/keys operate the same way, .entries()
retrieving (StorageKey, Codec)[]
and .keys()
retrieving StorageKey[]
While we can retrieve only the keys for a specific era, using an argument for the first part of the double map (as defined here, an EraIndex
):
In addition to querying the latest storage, you can make storage queries at a specific blockhash. Be aware that the node applies a pruning strategy and typically only keeps the last 256 blocks, unless run in archive mode.
The example below shows how to create a transaction to make a transfer from one account to another.
In addition to the signAndSend
helper on transactions, .paymentInfo
(with the exact same parameters) are also exposed. Using the same sender, it applies a dummy signature to the transaction and then gets the fee estimation via RPC.
Assuming you are sending a tx via .signAndSend
, the callback yields information around the tx pool status as well as any events when isInBlock
or isFinalized
. If an extrinsic fails via system.ExtrinsicFailed
event, you can retrieve the error if defined as an enum on a module.
As of the @polkadot/api
2.3.1 additional result fields are exposed. Firstly there is dispatchInfo: DispatchInfo
which occurs in both ExtrinsicSuccess
& ExtrinsicFailed
events. Additionally, on failures the dispatchError: DispatchError
is exposed. With this in mind, the above example can be simplified to be:
The section above shows you how to listen for the result of a regular extrinsic. However, Sudo extrinsics do not directly report the success or failure of the underlying call. Instead, a Sudo transaction will return Sudid(result)
, where result
will be the information you are looking for.
To properly parse this information, we will follow the steps above, but then specifically peek into the event data to find the final result:
For most runtime modules, transactions need to be signed, and validation for this happens on the node side. There are, however, modules that accept unsigned extrinsics. An example would be the Polkadot/Kusama token claims (here, used as an example).
The signing is indicated by the first byte in the transaction, so in this case, we have called .send
on it (no .sign
or .signAndSend
), so it will be sent using the unsigned state without the signature attached.
Note: The status event is only available on providers that support subscriptions, such as WSProvider
or ScProvider
. On HttpProvider
, which does not have bi-directional capabilities. There are no subscriptions, so it cannot listen to the events that are emitted by the transaction pool. In the case of HttpProvider
the result, object returned will always be the non-unique transaction hash.
Polkadot/Substrate provides a utility.batch
method that can be used to send a number of transactions at once. These are then executed from a single sender (single nonce specified) in sequence. This is very useful in many cases. For instance, if you wish to create a payout for a validator for multiple eras, you can use this method. Likewise, you can send several transfers at once or batch different types of transactions.
The fee for a batch transaction can be estimated similar to the fee for a single transaction using the exposed .paymentInfo
helper method described earlier, and it is usually less than the sum of the fees for each transaction.
The system.account
query will always contain the current state, i.e. it will reflect the nonce for the last known block. As such when sending multiple transactions in quick succession (see batching above), there may be transactions in the pool with the same nonce that signAndSend
would apply - this call does not do any magic. It simply reads the state for the nonce. Since we can specify options for the signAndSend
operation, we can override the nonce, either by manually incrementing it or querying it via rpc.system.accountNextIndex
.
As a convenience function, the accountNextIndex
can be omitted by specifying a nonce of -1
, allowing the API to do the lookup. In this case, the above example can be simplified even further:
The latter form is preferred since it dispatches the RPC calls for nonce and blockHash (used for mortality) in parallel. This approach will yield a better throughput, especially with the above bulk example.