Skip to content

Commit 5e5e72b

Browse files
authored
Add metadataVer cli arg for polkadot-types-internal-metadata, and ensure backwards compatibility with v14 (#6063)
* Ensure ws util has added options * Ensure backwards compatibility
1 parent c5aeea2 commit 5e5e72b

File tree

2 files changed

+100
-12
lines changed

2 files changed

+100
-12
lines changed

packages/typegen/src/metadataMd.ts

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ import polkadotRpc from '@polkadot/types-support/metadata/v15/polkadot-rpc';
3131
import polkadotVer from '@polkadot/types-support/metadata/v15/polkadot-ver';
3232
import substrateMeta from '@polkadot/types-support/metadata/v15/substrate-hex';
3333
import { isHex, stringCamelCase, stringLowerFirst } from '@polkadot/util';
34+
import { blake2AsHex } from '@polkadot/util-crypto';
3435

35-
import { assertFile, getMetadataViaWs, getRpcMethodsViaWs } from './util/index.js';
36+
import { assertFile, getMetadataViaWs, getRpcMethodsViaWs, getRuntimeVersionViaWs } from './util/index.js';
3637

3738
interface SectionItem {
3839
link?: string;
@@ -318,6 +319,63 @@ function addRuntime (_runtimeDesc: string, registry: Registry): string {
318319
});
319320
}
320321

322+
/** @internal */
323+
function addLegacyRuntime (_runtimeDesc: string, _registry: Registry, apis?: ApiDef[]) {
324+
return renderPage({
325+
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.',
326+
sections: Object
327+
.keys(definitions)
328+
.filter((key) => Object.keys(definitions[key as 'babe'].runtime || {}).length !== 0)
329+
.sort()
330+
.reduce((all: Section[], _sectionName): Section[] => {
331+
Object
332+
.entries(definitions[_sectionName as 'babe'].runtime || {})
333+
.forEach(([apiName, versions]) => {
334+
versions
335+
.sort((a, b) => b.version - a.version)
336+
.forEach(({ methods, version }, index) => {
337+
if (apis) {
338+
// if we are passing the api hashes and we cannot find this one, skip it
339+
const apiHash = blake2AsHex(apiName, 64);
340+
const api = apis.find(([hash]) => hash === apiHash);
341+
342+
if (!api || api[1] !== version) {
343+
return;
344+
}
345+
} else if (index) {
346+
// we only want the highest version
347+
return;
348+
}
349+
350+
const container: Section = { items: [], name: apiName };
351+
352+
all.push(container);
353+
354+
Object
355+
.entries(methods)
356+
.sort(([a], [b]) => a.localeCompare(b))
357+
.forEach(([methodName, { description, params, type }]): void => {
358+
const args = params.map(({ name, type }): string => {
359+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
360+
return name + ': `' + type + '`';
361+
}).join(', ');
362+
363+
container.items.push({
364+
interface: '`' + `api.call.${stringCamelCase(apiName)}.${stringCamelCase(methodName)}` + '`',
365+
name: `${stringCamelCase(methodName)}(${args}): ${'`' + type + '`'}`,
366+
runtime: '`' + `${apiName}_${methodName}` + '`',
367+
summary: description
368+
});
369+
});
370+
});
371+
});
372+
373+
return all;
374+
}, []).sort(sortByName),
375+
title: 'Runtime'
376+
});
377+
}
378+
321379
/** @internal */
322380
function addConstants (runtimeDesc: string, { lookup, pallets }: MetadataLatest): string {
323381
return renderPage({
@@ -504,28 +562,40 @@ function writeFile (name: string, ...chunks: any[]): void {
504562
writeStream.end();
505563
}
506564

507-
interface ArgV { chain?: string; endpoint?: string; }
565+
interface ArgV { chain?: string; endpoint?: string; metadataVer?: number; }
508566

509567
async function mainPromise (): Promise<void> {
510-
const { chain, endpoint } = yargs(hideBin(process.argv)).strict().options({
568+
const { chain, endpoint, metadataVer } = yargs(hideBin(process.argv)).strict().options({
511569
chain: {
512570
description: 'The chain name to use for the output (defaults to "Substrate")',
513571
type: 'string'
514572
},
515573
endpoint: {
516574
description: 'The endpoint to connect to (e.g. wss://kusama-rpc.polkadot.io) or relative path to a file containing the JSON output of an RPC state_getMetadata call',
517575
type: 'string'
576+
},
577+
metadataVer: {
578+
description: 'The metadata version to use for generating type information. This will use state_call::Metadata_metadata_at_version to query metadata',
579+
type: 'number'
518580
}
519581
}).argv as ArgV;
520582

583+
/**
584+
* This is unique to when the endpoint arg is used. Since the endpoint requires us to query the chain, it may query the chains
585+
* rpc state_getMetadata method to get metadata, but this restricts us to v14 only. Therefore we must also check if the `metadataVer` is passed
586+
* in as well. These checks will help us decide if we are using v14 or newer.
587+
*/
588+
const useV14Metadata = endpoint && ((metadataVer && metadataVer < 15) || !metadataVer);
521589
const chainName = chain || 'Substrate';
522590
let metaHex: HexString;
523591
let rpcMethods: string[] | undefined;
592+
let runtimeApis: ApiDef[] | undefined;
524593

525594
if (endpoint) {
526595
if (endpoint.startsWith('wss://') || endpoint.startsWith('ws://')) {
527-
metaHex = await getMetadataViaWs(endpoint);
596+
metaHex = await getMetadataViaWs(endpoint, metadataVer);
528597
rpcMethods = await getRpcMethodsViaWs(endpoint);
598+
runtimeApis = await getRuntimeVersionViaWs(endpoint);
529599
} else {
530600
metaHex = (
531601
JSON.parse(
@@ -544,9 +614,16 @@ async function mainPromise (): Promise<void> {
544614
metaHex = substrateMeta;
545615
}
546616

617+
let metadata: Metadata;
547618
const registry = new TypeRegistry();
548-
const opaqueMetadata = registry.createType('Option<OpaqueMetadata>', registry.createType('Raw', metaHex).toU8a()).unwrap();
549-
const metadata = new Metadata(registry, opaqueMetadata.toHex());
619+
620+
if (useV14Metadata) {
621+
metadata = new Metadata(registry, metaHex);
622+
} else {
623+
const opaqueMetadata = registry.createType('Option<OpaqueMetadata>', registry.createType('Raw', metaHex).toU8a()).unwrap();
624+
625+
metadata = new Metadata(registry, opaqueMetadata.toHex());
626+
}
550627

551628
registry.setMetadata(metadata);
552629

@@ -556,7 +633,9 @@ async function mainPromise (): Promise<void> {
556633

557634
writeFile(`${docRoot}/rpc.md`, addRpc(runtimeDesc, rpcMethods));
558635

559-
writeFile(`${docRoot}/runtime.md`, addRuntime(runtimeDesc, registry));
636+
useV14Metadata
637+
? writeFile(`${docRoot}/runtime.md`, addLegacyRuntime(runtimeDesc, registry, runtimeApis))
638+
: writeFile(`${docRoot}/runtime.md`, addRuntime(runtimeDesc, registry));
560639

561640
writeFile(`${docRoot}/constants.md`, addConstants(runtimeDesc, latest));
562641
writeFile(`${docRoot}/storage.md`, addStorage(runtimeDesc, latest));

packages/typegen/src/util/wsMeta.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import type { HexString } from '@polkadot/util/types';
55

66
import { promiseTracker } from '@polkadot/api/promise/decorateMethod';
7-
import { stringify } from '@polkadot/util';
7+
import { TypeRegistry } from '@polkadot/types';
8+
import { stringify, u8aToHex } from '@polkadot/util';
89
import { WebSocket } from '@polkadot/x-ws';
910

10-
async function getWsData <T> (endpoint: string, method: 'rpc_methods' | 'state_getMetadata' | 'state_getRuntimeVersion'): Promise<T> {
11+
async function getWsData <T> (endpoint: string, method: 'rpc_methods' | 'state_call' | 'state_getMetadata' | 'state_getRuntimeVersion', params?: string[]): Promise<T> {
1112
return new Promise((resolve, reject): void => {
1213
const tracker = promiseTracker<T>(resolve, reject);
1314

@@ -26,7 +27,9 @@ async function getWsData <T> (endpoint: string, method: 'rpc_methods' | 'state_g
2627

2728
websocket.onopen = (): void => {
2829
console.log('connected');
29-
websocket.send(`{"id":"1","jsonrpc":"2.0","method":"${method}","params":[]}`);
30+
params
31+
? websocket.send(`{"id":"1","jsonrpc":"2.0","method":"${method}","params":[${params.map((param) => `"${param}"`).join(',')}]}`)
32+
: websocket.send(`{"id":"1","jsonrpc":"2.0","method":"${method}","params":[]}`);
3033
};
3134

3235
websocket.onmessage = (message: { data: string }): void => {
@@ -44,8 +47,14 @@ async function getWsData <T> (endpoint: string, method: 'rpc_methods' | 'state_g
4447
});
4548
}
4649

47-
export async function getMetadataViaWs (endpoint: string): Promise<HexString> {
48-
return getWsData<HexString>(endpoint, 'state_getMetadata');
50+
export async function getMetadataViaWs (endpoint: string, metadataVer?: number): Promise<HexString> {
51+
const registry = new TypeRegistry();
52+
53+
if (metadataVer) {
54+
return await getWsData<HexString>(endpoint, 'state_call', ['Metadata_metadata_at_version', u8aToHex(registry.createType('u32', metadataVer).toU8a())]);
55+
} else {
56+
return await getWsData<HexString>(endpoint, 'state_getMetadata');
57+
}
4958
}
5059

5160
export async function getRpcMethodsViaWs (endpoint: string): Promise<string[]> {

0 commit comments

Comments
 (0)