subculture
Version:
Polkadot.js and Substrate development utilities.
171 lines (165 loc) • 4.74 kB
JavaScript
import { encodeAddress, decodeAddress } from '@polkadot/keyring';
import { isHex, hexToU8a } from '@polkadot/util';
import limit from 'p-limit';
import { range } from 'lodash';
/**
*
* Validate a substrate address
*
* @param address AddressOrPair
* @param ss58Format number
* @returns boolean
*/
const isValid = (address, ss58Format) => {
try {
encodeAddress(isHex(address)
? hexToU8a(address)
: decodeAddress(address.toString(), undefined, ss58Format));
return true;
}
catch (error) {
return false;
}
};
/**
*
* Shorten an address. Usefull in small UI lists etc.
*
* @param address AddressOrPair
* @param sliceStart number
* @param sliceEnd number
* @param seperator string
* @returns string
*/
const shorten = (address, sliceStart = 6, sliceEnd = 4, seperator) => {
const addressString = address.toString();
return `${addressString.slice(0, sliceStart)}${seperator}${addressString.slice(-sliceEnd)}`;
};
/**
*
* Last four characters of an address
*
* @param address AddressOrPair
* @returns string
*/
const lastFour = (address) => address.toString().slice(-4);
/**
*
* Get the latest block
*
* @param api ApiPromise
* @returns SignedBlock
*/
const latest = async (api) => api.rpc.chain.getBlock();
/**
*
* Get the block at a certain blocknumber if it exists.
*
* @param api ApiPromise
* @param nr number
* @returns SignedBlock | null
*/
const at = async (api, nr) => {
try {
return await api.rpc.chain.getBlockHash(nr).then(hash => api.rpc.chain.getBlock(hash));
}
catch (error) {
return null;
}
};
/**
*
* Get the block number of a block.
*
* @param block SignedBlock
* @returns number
*/
const numberOf = (block) => {
return parseInt(block.block.header.number.toString().replace(/,/g, ''));
};
/**
*
* Tails the chain for new blocks from a given blocknumber.
* Awaits the callback for sequential processing.
*
* @nb If average callback processing time takes longer time than the average chain block time
* the tailing will start to lagg behind the chain block production.
*
* @param api ApiPromise
* @param nr number - block number to tail from
* @param cb function - callback to invoke on new block
* @returns function - unsubscribe
*/
const tail = async (api, nr, cb) => {
const block = await at(api, nr);
if (!block) {
return await api.rpc.chain.subscribeFinalizedHeads(header => {
return api.rpc.chain.getBlock(header.hash).then(async (block) => {
return await cb(block);
});
});
}
else {
await cb(block);
return tail(api, nr + 1, cb);
}
};
/**
*
* Get a slice of blocks
*
* @param api ApiPromise
* @param from number - start block
* @param to number - end block
* @returns Awaitable<SignedBlock[]>
*/
const slice = async (api, from, to, concurrency = 8) => {
const pool = limit(concurrency);
return (await Promise.all(range(from, to).map(nr => pool(async () => {
const block = await at(api, nr);
if (block) {
return block;
}
})))).filter((block) => Boolean(block));
};
/**
*
* Fetch Events for a given block and return the pair.
*
* @param api ApiPromise
* @param block SignedBlock
* @returns BlockEventsPair
*/
const mapEventsToBlock = async (api, block) => {
const blockEvents = await (await api.at(block.block.header.hash.toHex())).query.system.events();
return [block, blockEvents];
};
/**
*
* Get the extrinsics in a block mapped by its events.
*
* @param events Vec<EventRecord>
* @param block SignedBlock
* @param filter.method string
* @returns GenericExtrinsic<AnyTuple>[]
*/
const mapToExtrinsics = (block, events, filter) => {
return block.block.extrinsics.filter((ex, index) => Boolean(events.find(event => event.phase.isApplyExtrinsic &&
event.phase.asApplyExtrinsic.eq(index) &&
(!filter?.method || ex.method.method.toString() === filter.method))));
};
export { at, isValid, lastFour, latest, mapEventsToBlock, mapToExtrinsics, numberOf, shorten, slice, tail };
t => event.phase.isApplyExtrinsic &&
event.phase.asApplyExtrinsic.eq(index) &&
(!filter?.method || ex.method.method.toString() === filter.method))));
};
exports.at = at;
exports.isValid = isValid;
exports.lastFour = lastFour;
exports.latest = latest;
exports.mapEventsToBlock = mapEventsToBlock;
exports.mapToExtrinsics = mapToExtrinsics;
exports.numberOf = numberOf;
exports.shorten = shorten;
exports.slice = slice;
exports.tail = tail;