Blocks

The building blocks for each blockchain. As such there are a number of examples for working with blocks and headers, that could be useful.

How do I retrieve the header/extrinsic hash from blocks?

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 implements the .hash => Hash getter, we can simply use this to view the actual hash.

// returns Hash
const blockHash = await this.api.rpc.chain.getBlockHash(blockNumber);
// returns SignedBlock
const signedBlock = await this.api.rpc.chain.getBlock(blockHash);

// the hash for the block, always via header (Hash -> toHex()) - will be
// the same as blockHash above (also available on any header retrieved,
// subscription or once-off)
console.log(signedBlock.header.hash.toHex());

// the hash for each extrinsic in the block
signedBlock.block.extrinsics.forEach((ex, index) => {
  console.log(index, ex.hash.toHex());
});

How do I extract the block author?

The block author is encoded inside the consensus logs for the block. To extract, 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).

// subscribe to all new headers (with extended info)
api.derive.chain.subscribeNewHeads((header) => {
  console.log(`#${header.number}: ${header.author}`);
});

For a single header only, the derives also contain a getHeader, which once again returns a header extended with the author -

// retrieve the last header (hash optional)
const header = await api.derive.chain.getHeader();

console.log(`#${header.number}: ${header.author}`);

How do I view extrinsic information?

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 do 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 -

// no blockHash is specified, so we retrieve the latest
const signedBlock = await this.api.rpc.chain.getBlock();

// the information for each of the contained extrinsics
signedBlock.block.extrinsics.forEach((ex, index) => {
  // the extrinsics are decoded by the API, human-like view
  console.log(index, ex.toHuman());

  const { isSigned, meta, method: { args, method, section } } = ex;

  // explicit display of name, args & documentation
  console.log(`${section}.${method}(${args.map((a) => a.toString()).join(', ')})`);
  console.log(meta.documentation.map((d) => d.toString()).join('\n'));

  // signer/nonce info
  if (isSigned) {
    console.log(`signer=${ex.signer.toString()}, nonce=${ex.nonce.toString()}`);
  }
});

In the above .toHuman() is used to format 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 pallete that executed this transaction.

How do I map extrinsics to their events?

While the blocks contain the extrinsics, the system event storage will contain the events and the details needed to allow for a mapping between. 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 the order of the extrinsics as found.

To perform a mapping between the two, we need information from both sources.

// no blockHash is specified, so we retrieve the latest
const signedBlock = await this.api.rpc.chain.getBlock();
const allRecords = await api.query.system.events(signedBlock.header.hash);

// map between the extrinsics and events
signedBlock.block.extrinsics.forEach(({ method: { method, section } }, index) => {
  // filter the specific events based on the phase and then the
  // index of our extrinsic in the block
  const events = allRecords
    .filter(({ phase }) =>
      phase.isApplyExtrinsic &&
      phase.asApplyExtrinsic.eq(index)
    )
    .map(({ event }) => `${event.section}.${event.method}`);

  console.log(`${section}.${method}:: ${events.join(', ') || 'no events'}`);
)};