@substrate/api-sidecar
Version:
REST service that makes it easy to interact with blockchain nodes built using Substrate's FRAME framework.
202 lines • 10.3 kB
JavaScript
;
// Copyright 2017-2025 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractPalletsService = void 0;
const util_1 = require("@polkadot/util");
const http_errors_1 = require("http-errors");
const http_errors_2 = require("http-errors");
const AbstractService_1 = require("./AbstractService");
class AbstractPalletsService extends AbstractService_1.AbstractService {
getPalletMetadataType(meta, metadataFieldType) {
return this.getProperty(meta, metadataFieldType);
}
getProperty(obj, key) {
return obj[key];
}
/**
* Find a pallet's metadata info.
*
* @param palletId identifier for a FRAME pallet as a pallet name or index.
*/
findPalletMeta(adjustedMetadata, palletId, metadataFieldType) {
const pallets = adjustedMetadata['pallets'];
const palletMetaType = this.getPalletMetadataType(pallets[0], metadataFieldType);
const filtered = pallets.filter((mod) => !mod[metadataFieldType].isEmpty);
const { isValidPalletName, isValidPalletIndex, parsedPalletId } = this.validPalletId(pallets, palletId);
let palletMeta;
let palletIdx;
if (isValidPalletIndex) {
palletIdx = parsedPalletId;
for (const [sectionIdx, section] of filtered.entries()) {
const idx = section.index.eqn(255) ? sectionIdx : section.index.toNumber();
if (idx === palletIdx) {
palletMeta = section;
break;
}
}
}
else if (isValidPalletName) {
for (const [sectionIdx, section] of filtered.entries()) {
if (section.name.toLowerCase() === palletId.toLowerCase()) {
// ModuleMetadataV11 and lower have an `index` but they use 255 as a reserve value to signify
// that they are meaningless. So if the index is 255 we use its index in the filtered array
// of modules. But if the index is something else than we use `ModuleMetadataV12.index`.
// The reason they use a reserve value is that all previous ModuleMetadata versions actually
// extend the latest. So since the intro of ModuleMetadataV12 all versions have `index` in
// polkadot-js, but at the substrate level, only versions >= 12 have pallet `index`.
// https://github.com/polkadot-js/api/pull/2599
// https://github.com/paritytech/substrate/pull/6969
// https://github.com/polkadot-js/api/issues/2596
palletIdx = section.index.eqn(255) ? sectionIdx : section.index.toNumber();
palletMeta = section;
break;
}
}
}
if (isValidPalletIndex === false && isValidPalletName === false) {
throw new http_errors_1.BadRequest(`"${palletId}" was not recognized as a queryable pallet.`);
}
else if (!palletMeta || palletIdx === undefined || palletIdx < 0) {
throw new http_errors_1.BadRequest(`no queryable ${metadataFieldType} items found for palletId "${palletId}"`);
}
return [palletMeta, palletIdx];
}
validPalletId(modules, palletId) {
// Either a pallet name (string) or a pallet index (number)
const parsedPalletId = AbstractPalletsService.palletIdxOrName(palletId);
const isValidPalletName = typeof parsedPalletId === 'string' &&
modules.some((meta) => meta.name.toString().toLowerCase() === palletId.toLowerCase());
const isValidPalletIndex = typeof parsedPalletId === 'number' &&
modules.some((meta, idx) => (meta.index.eqn(255) ? idx === parsedPalletId : meta.index.eqn(parsedPalletId)));
return {
isValidPalletName,
isValidPalletIndex,
parsedPalletId,
};
}
/**
* Identify if a pallet Identifier should be an index or a string. If it should
* be an index return a number and if it should be a name return a string.
*
* @param palletId FRAME pallet identifier as a pallet name or index
*/
static palletIdxOrName(palletId) {
const maybeIdx = Number(palletId);
if (Number.isInteger(maybeIdx)) {
return maybeIdx;
}
return palletId;
}
/**
* Find the an item's metadata within a given pallets' metadata based on a provided type.
*
* @param historicApi Decorated historic api
* @param palletMeta the metadata of the pallet that contains the error item
* @param palletItemId name of the error item in camel or pascal case
* @param metadataFieldType name of the metadata field to be queried
*
*/
findPalletFieldItemMeta(historicApi, palletMeta, palletItemId, metadataFieldType) {
let palletItemIdx = -1;
let palletItemMeta;
if (metadataFieldType === 'storage') {
[palletItemIdx, palletItemMeta] = this.getStorageItemMeta(palletMeta, palletItemIdx, palletItemId);
}
else if (metadataFieldType === 'errors') {
[palletItemIdx, palletItemMeta] = this.getErrorItemMeta(historicApi, palletMeta, palletItemIdx, palletItemId);
}
else if (metadataFieldType === 'events') {
[palletItemIdx, palletItemMeta] = this.getEventItemMeta(historicApi, palletMeta, palletItemIdx, palletItemId);
}
else if (metadataFieldType === 'calls') {
[palletItemIdx, palletItemMeta] = this.getDispatchablesItemMeta(palletMeta, palletItemIdx, palletItemId);
}
else {
[palletItemIdx, palletItemMeta] = this.getConstItemMeta(palletMeta, palletItemIdx, palletItemId);
}
if (palletItemIdx === -1) {
throw new http_errors_2.InternalServerError(`Could not find ${metadataFieldType} item ("${palletItemId}") in metadata. ${metadataFieldType} item names are expected to be in camel case, e.g. 'storageItemId'`);
}
return palletItemMeta;
}
getDispatchablesItemMeta(palletMeta, dispatchableItemMetaIdx, dispatchableItemId) {
const palletName = (0, util_1.stringCamelCase)(palletMeta.name);
const dispatchables = this.api.tx[palletName];
if (palletMeta.calls.isEmpty) {
throw new http_errors_2.InternalServerError(`No dispatchable items found in ${palletMeta.name.toString()}'s metadata`);
}
for (const [, val] of Object.entries(dispatchables)) {
const item = val.meta;
if ((0, util_1.stringCamelCase)(item.name).toLowerCase() === dispatchableItemId.toLowerCase()) {
dispatchableItemMetaIdx = val.meta.index.toNumber();
break;
}
}
return [
dispatchableItemMetaIdx,
Object.entries(dispatchables)[dispatchableItemMetaIdx],
];
}
getConstItemMeta(palletMeta, constItemMetaIdx, constItemId) {
if (palletMeta.constants.isEmpty) {
throw new http_errors_2.InternalServerError(`No const items found in ${palletMeta.name.toString()}'s metadata`);
}
const palletMetaConsts = palletMeta.constants;
constItemMetaIdx = palletMetaConsts.findIndex((item) => item.name.toLowerCase() === constItemId.toLowerCase());
return [constItemMetaIdx, palletMetaConsts[constItemMetaIdx]];
}
getErrorItemMeta(historicApi, palletMeta, errorItemMetaIdx, errorItemId) {
const palletName = (0, util_1.stringCamelCase)(palletMeta.name);
const errors = historicApi.errors[palletName];
if (palletMeta.errors.isEmpty) {
throw new http_errors_2.InternalServerError(`No error items found in ${palletMeta.name.toString()}'s metadata`);
}
for (const [, val] of Object.entries(errors)) {
const item = val.meta;
if (item.name.toLowerCase() === errorItemId.toLowerCase()) {
errorItemMetaIdx = val.meta.index.toNumber();
break;
}
}
return [errorItemMetaIdx, Object.entries(errors)[errorItemMetaIdx]];
}
getEventItemMeta(historicApi, palletMeta, eventItemMetaIdx, eventItemId) {
const palletName = (0, util_1.stringCamelCase)(palletMeta.name);
const events = historicApi.events[palletName];
if (palletMeta.events.isEmpty) {
throw new http_errors_2.InternalServerError(`No event items found in ${palletMeta.name.toString()}'s metadata`);
}
for (const [, val] of Object.entries(events)) {
const item = val.meta;
if (item.name.toLowerCase() === eventItemId.toLowerCase()) {
eventItemMetaIdx = val.meta.index.toNumber();
break;
}
}
return [eventItemMetaIdx, Object.entries(events)[eventItemMetaIdx]];
}
getStorageItemMeta(palletMeta, storageItemMetaIdx, storageItemId) {
if (palletMeta.storage.isNone) {
throw new http_errors_2.InternalServerError(`No storage items found in ${palletMeta.name.toString()}'s metadata`);
}
const palletMetaStorage = palletMeta.storage.unwrap().items;
storageItemMetaIdx = palletMetaStorage.findIndex((item) => item.name.toLowerCase() === storageItemId.toLowerCase());
return [storageItemMetaIdx, palletMetaStorage[storageItemMetaIdx]];
}
}
exports.AbstractPalletsService = AbstractPalletsService;
//# sourceMappingURL=AbstractPalletsService.js.map