diff --git a/packages/typegen/src/metadataMd.ts b/packages/typegen/src/metadataMd.ts index a16f5d470a2..f34615f93e3 100644 --- a/packages/typegen/src/metadataMd.ts +++ b/packages/typegen/src/metadataMd.ts @@ -1,10 +1,10 @@ // Copyright 2017-2025 @polkadot/typegen authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest, SiLookupTypeId } from '@polkadot/types/interfaces'; +import type { MetadataLatest, RuntimeApiMethodMetadataV15, SiLookupTypeId } from '@polkadot/types/interfaces'; import type { PortableRegistry } from '@polkadot/types/metadata'; import type { Text } from '@polkadot/types/primitive'; -import type { Codec, DefinitionRpcParam } from '@polkadot/types/types'; +import type { Codec, DefinitionCall, DefinitionRpcParam, DefinitionsCall, Registry } from '@polkadot/types/types'; import type { HexString } from '@polkadot/util/types'; import fs from 'node:fs'; @@ -17,13 +17,16 @@ import { Metadata, TypeRegistry, Vec } from '@polkadot/types'; import * as definitions from '@polkadot/types/interfaces/definitions'; import { getStorage as getSubstrateStorage } from '@polkadot/types/metadata/decorate/storage/getStorage'; import { unwrapStorageType } from '@polkadot/types/util'; -import kusamaMeta, { rpc as kusamaRpc, version as kusamaVer } from '@polkadot/types-support/metadata/static-kusama'; -import polkadotMeta, { rpc as polkadotRpc, version as polkadotVer } from '@polkadot/types-support/metadata/static-polkadot'; -import substrateMeta from '@polkadot/types-support/metadata/static-substrate'; +import kusamaMeta from '@polkadot/types-support/metadata/v15/kusama-hex'; +import kusamaRpc from '@polkadot/types-support/metadata/v15/kusama-rpc'; +import kusamaVer from '@polkadot/types-support/metadata/v15/kusama-ver'; +import polkadotMeta from '@polkadot/types-support/metadata/v15/polkadot-hex'; +import polkadotRpc from '@polkadot/types-support/metadata/v15/polkadot-rpc'; +import polkadotVer from '@polkadot/types-support/metadata/v15/polkadot-ver'; +import substrateMeta from '@polkadot/types-support/metadata/v15/substrate-hex'; import { isHex, stringCamelCase, stringLowerFirst } from '@polkadot/util'; -import { blake2AsHex } from '@polkadot/util-crypto'; -import { assertFile, getMetadataViaWs, getRpcMethodsViaWs, getRuntimeVersionViaWs } from './util/index.js'; +import { assertFile, getMetadataViaWs, getRpcMethodsViaWs } from './util/index.js'; interface SectionItem { link?: string; @@ -218,58 +221,83 @@ function addRpc (_runtimeDesc: string, rpcMethods?: string[]): string { } /** @internal */ -function addRuntime (_runtimeDesc: string, apis?: ApiDef[]): string { +function getMethods (registry: Registry, methods: Vec) { + const result: Record = {}; + + methods.forEach((m) => { + const { docs, inputs, name, output } = m; + + result[name.toString()] = { + description: docs.map((d) => d.toString()).join(), + params: inputs.map(({ name, type }) => { + return { name: name.toString(), type: registry.lookup.getName(type) || registry.lookup.getTypeDef(type).type }; + }), + type: registry.lookup.getName(output) || registry.lookup.getTypeDef(output).type + }; + }); + + return result; +} + +/** @internal */ +function getRuntimeDefViaMetadata (registry: Registry) { + const result: DefinitionsCall = {}; + const { apis } = registry.metadata; + + for (let i = 0, count = apis.length; i < count; i++) { + const { methods, name } = apis[i]; + + result[name.toString()] = [{ + methods: getMethods(registry, methods), + // We set the version to 0 here since it will not be relevant when we are grabbing the runtime apis + // from the Metadata. + version: 0 + }]; + } + + return Object.entries(result); +} + +function runtimeSections (registry: Registry) { + const sections = getRuntimeDefViaMetadata(registry); + const all = []; + + for (let j = 0, jcount = sections.length; j < jcount; j++) { + const [_section, secs] = sections[j]; + const sec = secs[0]; + + const section = stringCamelCase(_section); + const methods = Object.entries(sec.methods); + + const container: Section = { items: [], name: section }; + + all.push(container); + + methods + .sort(([a], [b]) => a.localeCompare(b)) + .forEach(([methodName, { description, params, type }]): void => { + const args = params.map(({ name, type }): string => { + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + return name + ': `' + type + '`'; + }).join(', '); + + container.items.push({ + interface: '`' + `api.call.${stringCamelCase(section)}.${stringCamelCase(methodName)}` + '`', + name: `${stringCamelCase(methodName)}(${args}): ${'`' + type + '`'}`, + runtime: '`' + `${section}_${methodName}` + '`', + summary: description + }); + }); + } + + return all.sort(sortByName); +} + +/** @internal */ +function addRuntime (_runtimeDesc: string, registry: Registry): string { return renderPage({ description: 'The following section contains known runtime calls that may be available on specific runtimes (depending on configuration and available pallets). These call directly into the WASM runtime for queries and operations.', - sections: Object - .keys(definitions) - .filter((key) => Object.keys(definitions[key as 'babe'].runtime || {}).length !== 0) - .sort() - .reduce((all: Section[], _sectionName): Section[] => { - Object - .entries(definitions[_sectionName as 'babe'].runtime || {}) - .forEach(([apiName, versions]) => { - versions - .sort((a, b) => b.version - a.version) - .forEach(({ methods, version }, index) => { - if (apis) { - // if we are passing the api hashes and we cannot find this one, skip it - const apiHash = blake2AsHex(apiName, 64); - const api = apis.find(([hash]) => hash === apiHash); - - if (!api || api[1] !== version) { - return; - } - } else if (index) { - // we only want the highest version - return; - } - - const container: Section = { items: [], name: apiName }; - - all.push(container); - - Object - .entries(methods) - .sort(([a], [b]) => a.localeCompare(b)) - .forEach(([methodName, { description, params, type }]): void => { - const args = params.map(({ name, type }): string => { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - return name + ': `' + type + '`'; - }).join(', '); - - container.items.push({ - interface: '`' + `api.call.${stringCamelCase(apiName)}.${stringCamelCase(methodName)}` + '`', - name: `${stringCamelCase(methodName)}(${args}): ${'`' + type + '`'}`, - runtime: '`' + `${apiName}_${methodName}` + '`', - summary: description - }); - }); - }); - }); - - return all; - }, []).sort(sortByName), + sections: runtimeSections(registry), title: 'Runtime' }); } @@ -477,13 +505,11 @@ async function mainPromise (): Promise { const chainName = chain || 'Substrate'; let metaHex: HexString; let rpcMethods: string[] | undefined; - let runtimeApis: ApiDef[] | undefined; if (endpoint) { if (endpoint.startsWith('wss://') || endpoint.startsWith('ws://')) { metaHex = await getMetadataViaWs(endpoint); rpcMethods = await getRpcMethodsViaWs(endpoint); - runtimeApis = await getRuntimeVersionViaWs(endpoint); } else { metaHex = ( JSON.parse( @@ -498,13 +524,13 @@ async function mainPromise (): Promise { } else if (ALL_STATIC[chainName.toLowerCase()]) { metaHex = ALL_STATIC[chainName.toLowerCase()].meta; rpcMethods = ALL_STATIC[chainName.toLowerCase()].rpc?.methods; - runtimeApis = ALL_STATIC[chainName.toLowerCase()].ver?.apis; } else { metaHex = substrateMeta; } const registry = new TypeRegistry(); - const metadata = new Metadata(registry, metaHex); + const opaqueMetadata = registry.createType('Option', registry.createType('Raw', metaHex).toU8a()).unwrap(); + const metadata = new Metadata(registry, opaqueMetadata.toHex()); registry.setMetadata(metadata); @@ -514,7 +540,7 @@ async function mainPromise (): Promise { writeFile(`${docRoot}/rpc.md`, addRpc(runtimeDesc, rpcMethods)); - writeFile(`${docRoot}/runtime.md`, addRuntime(runtimeDesc, runtimeApis)); + writeFile(`${docRoot}/runtime.md`, addRuntime(runtimeDesc, registry)); writeFile(`${docRoot}/constants.md`, addConstants(runtimeDesc, latest)); writeFile(`${docRoot}/storage.md`, addStorage(runtimeDesc, latest));