diff --git a/README.md b/README.md index a2afac79e..88d00ca2e 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,6 @@ a 32-byte hex string (`0x` followed by 64 hexadecimal digits) that denotes the b - [`/block` fetch latest finalized block details.](/src/controllers/blocks/BlocksController.ts) -- [`/block` fetch latest finalized block details.](/src/controllers/blocks/BlocksController.ts) - - [`/block/NUMBER` fetch block details at the block identified by 'NUMBER`.](/src/controllers/blocks/BlocksController.ts) - [`/balance/ADDRESS` fetch balances for `ADDRESS` at latest finalized block.](src/controllers/accounts/AccountsBalanceInfoController.ts) @@ -104,16 +102,14 @@ a 32-byte hex string (`0x` followed by 64 hexadecimal digits) that denotes the b - [`/vesting/ADDRESS/NUMBER` fetch the vesting info for `ADDRESS` at the block identified by 'NUMBER`.](src/controllers/accounts/AccountsVestingInfoController.ts) -- [`/metadata` fetch chain metadata at latest finalized block.](src/controllers/runtime/RuntimeMetadataController.ts) - -- [`/metadata/NUMBER` fetch chain metadata at the block identified by 'NUMBER`.](src/controllers/runtime/RuntimeMetadataController.ts) - - [`/node/network` fetch information about the Substrate node's activity in the peer-to-peer network.](src/controllers/node/NodeNetworkController.ts) - [`/node/transaction-pool` fetch pending extrinsics from the Substrate node.](src/controllers/node/NodeTransactionPoolController.ts) - [`/node/version` fetch information about the Substrates node's implementation and versioning.](src/controllers/node/NodeVersionController.ts) +- [`/runtime/metadata` fetch the runtime metadata in decoded, JSON form.](src/controllers/runtime/RuntimeMetadataController.ts) (replaces `/metadata`) + - [`/runtime/code` fetch the Wasm code blob of the Substrate runtime.](src/controllers/runtime/RuntimeCodeController.ts) - [`/runtime/spec` version information of the Substrate runtime.](src/controllers/runtime/RuntimeSpecController.ts) diff --git a/openapi/openapi-proposal.yaml b/openapi/openapi-proposal.yaml index 33ae541cf..0c8529e18 100755 --- a/openapi/openapi-proposal.yaml +++ b/openapi/openapi-proposal.yaml @@ -944,7 +944,11 @@ paths: tags: - runtime summary: Get the runtime metadata in decoded, JSON form. - description: Returns the runtime metadata as a JSON object. + description: >- + Returns the runtime metadata as a JSON object. + Substrate Reference: + - FRAME Support: https://crates.parity.io/frame_support/metadata/index.html + - Knowledge Base: https://substrate.dev/docs/en/knowledgebase/runtime/metadata parameters: - name: at in: query diff --git a/src/controllers/runtime/RuntimeMetadataController.ts b/src/controllers/runtime/RuntimeMetadataController.ts new file mode 100644 index 000000000..b7470e6e5 --- /dev/null +++ b/src/controllers/runtime/RuntimeMetadataController.ts @@ -0,0 +1,50 @@ +import { ApiPromise } from '@polkadot/api'; +import { RequestHandler } from 'express'; + +import { RuntimeMetadataService } from '../../services'; +import AbstractController from '../AbstractController'; + +/** + * GET the chain's metadata. + * + * Query: + * - (Optional) `at`: Block hash or height at which to query. If not provided, queries + * finalized head. + * + * Returns: + * - Metadata object. + * + * Substrate Reference: + * - FRAME Support: https://crates.parity.io/frame_support/metadata/index.html + * - Knowledge Base: https://substrate.dev/docs/en/knowledgebase/runtime/metadata + */ +export default class RuntimeMetadataController extends AbstractController< + RuntimeMetadataService +> { + constructor(api: ApiPromise) { + super(api, '/runtime/metadata', new RuntimeMetadataService(api)); + this.initRoutes(); + } + + protected initRoutes(): void { + this.safeMountAsyncGetHandlers([['', this.getMetadata]]); + } + + /** + * Get the chain's latest metadata in a decoded, JSON format. + * + * @param _req Express Request + * @param res Express Response + */ + private getMetadata: RequestHandler = async ( + { query: { at } }, + res + ): Promise => { + const hash = await this.getHashFromAt(at); + + RuntimeMetadataController.sanitizedSend( + res, + await this.service.fetchMetadata(hash) + ); + }; +} diff --git a/src/controllers/runtime/index.ts b/src/controllers/runtime/index.ts index f4a06dbac..11161d0eb 100644 --- a/src/controllers/runtime/index.ts +++ b/src/controllers/runtime/index.ts @@ -1,2 +1,3 @@ export { default as RuntimeCode } from './RuntimeCodeController'; export { default as RuntimeSpec } from './RuntimeSpecController'; +export { default as RuntimeMetadata } from './RuntimeMetadataController'; diff --git a/src/main.ts b/src/main.ts index 90f2018ef..56703c1a7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -97,6 +97,7 @@ async function main() { ); const runtimeCodeController = new controllers.RuntimeCode(api); const runtimeSpecController = new controllers.RuntimeSpec(api); + const runtimeMetadataController = new controllers.RuntimeMetadata(api); const transactionDryRunController = new controllers.TransactionDryRun(api); // Create our App @@ -109,6 +110,7 @@ async function main() { nodeTransactionPoolController, runtimeCodeController, runtimeSpecController, + runtimeMetadataController, transactionDryRunController, ...v0Controllers, ], diff --git a/src/services/runtime/RuntimeMetadataService.spec.ts b/src/services/runtime/RuntimeMetadataService.spec.ts new file mode 100644 index 000000000..dec936542 --- /dev/null +++ b/src/services/runtime/RuntimeMetadataService.spec.ts @@ -0,0 +1,18 @@ +import { sanitizeNumbers } from '../../sanitize/sanitizeNumbers'; +import { blockHash789629, mockApi } from '../test-helpers/mock'; +import * as response789629 from '../test-helpers/responses/runtime/metadata789629.json'; +import { RuntimeMetadataService } from './RuntimeMetadataService'; + +const runtimeMetadataService = new RuntimeMetadataService(mockApi); + +describe('RuntimeMetadataService', () => { + describe('fetchMetadata', () => { + it('works when ApiPromise works (block 789629)', async () => { + expect( + sanitizeNumbers( + await runtimeMetadataService.fetchMetadata(blockHash789629) + ) + ).toStrictEqual(response789629); + }); + }); +}); diff --git a/src/services/runtime/RuntimeMetadataService.ts b/src/services/runtime/RuntimeMetadataService.ts new file mode 100644 index 000000000..0784134ff --- /dev/null +++ b/src/services/runtime/RuntimeMetadataService.ts @@ -0,0 +1,19 @@ +import { Metadata } from '@polkadot/types'; +import { BlockHash } from '@polkadot/types/interfaces'; + +import { AbstractService } from '../AbstractService'; + +export class RuntimeMetadataService extends AbstractService { + /** + * Fetch `Metadata` in decoded JSON form. + * + * @param hash `BlockHash` to make call at + */ + async fetchMetadata(hash: BlockHash): Promise { + const api = await this.ensureMeta(hash); + + const metadata = await api.rpc.state.getMetadata(hash); + + return metadata; + } +} diff --git a/src/services/runtime/index.ts b/src/services/runtime/index.ts index b246e8480..75f7e0126 100644 --- a/src/services/runtime/index.ts +++ b/src/services/runtime/index.ts @@ -1,2 +1,3 @@ export * from './RuntimeCodeService'; export * from './RuntimeSpecService'; +export * from './RuntimeMetadataService';