UNPKG

@gear-js/api

Version:

A JavaScript library that provides functionality to connect GEAR Component APIs.

140 lines (137 loc) 4.87 kB
import { isU8a, isHex, isNumber } from '@polkadot/util'; import { GetBlockError } from '../errors/blocks.errors.js'; import { CreateType } from '../metadata/create-type.js'; import '../metadata/programMetadata.js'; import 'node:assert'; import '@polkadot/types'; class GearBlock { api; subscribeNewHeads; constructor(api) { this.api = api; api.isReady.then(() => { this.subscribeNewHeads = api.derive.chain.subscribeNewHeads; }); } /** * Get data of particular block by blockNumber or blockHash * @param hashOrNumber * @returns */ async get(hashOrNumber) { const hash = isU8a(hashOrNumber) || isHex(hashOrNumber) ? hashOrNumber : await this.getBlockHash(+hashOrNumber); try { return await this.api.rpc.chain.getBlock(hash); } catch (error) { throw new GetBlockError(error.message, hash); } } /** * Get blockHash by number * @param number number of block * @returns blockHash */ async getBlockHash(number) { return await this.api.rpc.chain.getBlockHash(number); } /** * Get block number * @param hash * @returns Compact<BlockNumber> */ async getBlockNumber(hash) { const block = await this.get(hash); return block.block.header.number; } async getBlockTimestamp(blockOrHashOrNumber) { const block = isHex(blockOrHashOrNumber) || isU8a(blockOrHashOrNumber) || isNumber(blockOrHashOrNumber) ? await this.get(blockOrHashOrNumber) : blockOrHashOrNumber; const tsAsU8a = block.block.extrinsics.find((value) => value.method.method === 'set' && value.method.section === 'timestamp').data; const ts = CreateType.create('Compact<u64>', tsAsU8a); return ts; } /** * Get all extrinsic of particular block * @param blockHash hash of particular block * @returns Vec of extrinsics */ async getExtrinsics(blockHash) { return (await this.get(blockHash)).block.extrinsics; } /** * Get all events of particular block * @param blockHash hash of particular block * @returns Vec of events */ async getEvents(blockHash) { const apiAt = await this.api.at(blockHash); return apiAt.query.system.events(); } /** * Get hash of last finalized block * @returns Hash of finalized head */ async getFinalizedHead() { return this.api.rpc.chain.getFinalizedHead(); } async subscribeToHeadsFrom(from, cb, blocks = 'latest') { const fromBlockHash = isHex(from) ? from : await this.api.rpc.chain.getBlockHash(from); const fromHeader = await this.api.rpc.chain.getHeader(fromBlockHash); let blockNumber = fromHeader.number.toNumber(); const lastHeader = blocks === 'finalized' ? await this.api.rpc.chain.getHeader(await this.getFinalizedHead()) : await this.api.rpc.chain.getHeader(); let lastHeadNumber = lastHeader.number.toNumber(); let unsubscribed = false; const blockQueue = [fromHeader]; blockNumber++; let newBlockResolver = null; const processQueue = async () => { while (!unsubscribed) { if (blockQueue.length > 0) { const header = blockQueue.shift(); await cb(header); } else { await new Promise((resolve) => { newBlockResolver = resolve; }); } } }; const oldBlocksSub = async () => { while (!unsubscribed && lastHeadNumber >= blockNumber) { const hash = await this.api.rpc.chain.getBlockHash(blockNumber); const header = await this.api.rpc.chain.getHeader(hash); blockQueue.push(header); blockNumber++; if (newBlockResolver) { newBlockResolver(); newBlockResolver = null; } } }; const unsub = await this.api.rpc.chain[blocks === 'finalized' ? 'subscribeFinalizedHeads' : 'subscribeNewHeads'](async (header) => { lastHeadNumber = header.number.toNumber(); if (blockNumber > lastHeadNumber) { blockQueue.push(header); if (newBlockResolver) { newBlockResolver(); newBlockResolver = null; } } }); oldBlocksSub(); processQueue(); return () => { unsubscribed = true; if (newBlockResolver) { newBlockResolver(); } unsub(); }; } } export { GearBlock };