diff --git a/.github/workflows/update-api-docs.yml b/.github/workflows/update-api-docs.yml new file mode 100644 index 00000000..8bd23d7b --- /dev/null +++ b/.github/workflows/update-api-docs.yml @@ -0,0 +1,155 @@ +name: Update API Reference Docs + +on: + schedule: + - cron: "0 6 * * 5" + + workflow_dispatch: + inputs: + endpoint: + description: "WebSocket endpoint (blank = mainnet)" + required: false + default: "" + +env: + NODE_VERSION: "20" + GENERATOR_DIR: "src/subtensor-docs-gen" + DOCS_DIR: "docs/subtensor-api" + BRANCH_PREFIX: "docs/auto-update" + SUBTENSOR_WS: >- + ${{ + inputs.endpoint != '' && inputs.endpoint + || secrets.SUBTENSOR_WS_ENDPOINT != '' && secrets.SUBTENSOR_WS_ENDPOINT + || 'wss://entrypoint-finney.opentensor.ai:443' + }} + +jobs: + regenerate-docs: + name: Regenerate docs and open PR + runs-on: ubuntu-latest + + steps: + - name: Generate GitHub App token + uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 + id: app-token + with: + app-id: ${{ secrets.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Set up Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "yarn" + cache-dependency-path: ${{ env.GENERATOR_DIR }}/yarn.lock + + - name: Install generator dependencies + working-directory: ${{ env.GENERATOR_DIR }} + run: yarn install --frozen-lockfile + + - name: Generate API reference docs + working-directory: ${{ env.GENERATOR_DIR }} + env: + SUBTENSOR_WS: ${{ env.SUBTENSOR_WS }} + OUTPUT_DIR: "../../${{ env.DOCS_DIR }}" + run: yarn generate + timeout-minutes: 10 + + - name: Check for changes + id: diff + run: | + if git diff --quiet HEAD -- "$DOCS_DIR"; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "No changes in $DOCS_DIR β€” nothing to PR." + else + # Check whether changes go beyond the generation-date stamp. + # Extract added/removed content lines, then strip out lines + # that only update the "Generated from a live snapshot … on **DATE**" header. + SUBSTANTIVE=$(git diff HEAD -- "$DOCS_DIR" \ + | grep -E '^[+-]' \ + | grep -vE '^(\+\+\+|---) ' \ + | grep -vE 'Generated from a live snapshot of the Subtensor runtime on' \ + | wc -l) + + if [ "$SUBSTANTIVE" -eq 0 ]; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "Only generation-date changes detected β€” skipping PR." + git checkout -- "$DOCS_DIR" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + fi + + - name: Commit updated docs to new branch + if: steps.diff.outputs.changed == 'true' + run: | + DATE=$(date -u +%Y-%m-%d) + BRANCH="${BRANCH_PREFIX}-${DATE}" + echo "BRANCH=${BRANCH}" >> "$GITHUB_ENV" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git checkout -b "$BRANCH" + git add "$DOCS_DIR" + git commit -m "docs: regenerate API reference $(date -u '+%Y-%m-%d %H:%M UTC')" + git push origin "$BRANCH" + + - name: Build PR body + if: steps.diff.outputs.changed == 'true' + id: pr_body + run: | + STAT=$(git diff origin/main..HEAD -- "$DOCS_DIR" --stat | tail -6) + ADDED=$(git diff origin/main..HEAD -- "$DOCS_DIR" | grep -c '^+### ' || true) + REMOVED=$(git diff origin/main..HEAD -- "$DOCS_DIR" | grep -c '^-### ' || true) + CHANGED_FILES=$(git diff origin/main..HEAD -- "$DOCS_DIR" --name-only | sed 's/^/- /') + + { + echo 'body<> "$GITHUB_OUTPUT" + + - name: Open Pull Request + if: steps.diff.outputs.changed == 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh pr create \ + --title "docs: API reference update $(date -u +%Y-%m-%d)" \ + --head "$BRANCH" \ + --base main \ + --body "${{ steps.pr_body.outputs.body }}" \ + --reviewer chideraao diff --git a/.gitignore b/.gitignore index cdeec9eb..ac880a8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Dependencies -/node_modules +node_modules/ .obsidian docs/.obsidian diff --git a/docs/evm-tutorials/bridge-vtao.md b/docs/evm-tutorials/bridge-vtao.md index eccafa99..5f355817 100644 --- a/docs/evm-tutorials/bridge-vtao.md +++ b/docs/evm-tutorials/bridge-vtao.md @@ -6,7 +6,7 @@ title: "Token Bridging" This guide provides an overview of two related topics: -- how to moves TAO between Substrate-style wallets (SS58) and the Etherum style wallets on Bittensor EVM +- how to move TAO between Substrate-style wallets (SS58) and the Etherum style wallets on Bittensor EVM - how to use vTAO as a token bridge between Bittensor EVM and other EVM chains. :::info diff --git a/src/subtensor-docs-gen/README.md b/src/subtensor-docs-gen/README.md new file mode 100644 index 00000000..dfbac5de --- /dev/null +++ b/src/subtensor-docs-gen/README.md @@ -0,0 +1,98 @@ +# subtensor-docs-gen + +Generates the Markdown API reference at `docs/subtensor-api/` by connecting to a live +Subtensor node and introspecting the runtime metadata via `@polkadot/api`. + +Modelled after the [polkadot.js docs generator](https://github.com/polkadot-js/docs). + +## Prerequisites + +- Node.js 18+ +- Yarn 1.x (`npm install -g yarn` if not already installed) +- Access to a running Subtensor node (mainnet, testnet, or local) + +## Setup + +```bash +cd subtensor-docs-gen +yarn install +``` + +## Usage + +```bash +yarn generate +``` + +### Custom endpoint or output path + +```bash +SUBTENSOR_WS=wss://my-node.example.com:443 yarn generate + +# Write to a different directory +OUTPUT_DIR=/tmp/subtensor-docs yarn generate +``` + +## Output + +All files are written to `../docs/api-reference/`: + +| File | Contents | +| --------------- | ------------------------------------------------------- | +| `extrinsics.md` | All `api.tx.*` dispatchable calls | +| `events.md` | All `api.events.*` event definitions | +| `errors.md` | All `api.errors.*` error variants | +| `storage.md` | All `api.query.*` storage items | +| `constants.md` | All `api.consts.*` runtime constants (with live values) | +| `rpc.md` | Custom Subtensor JSON-RPC methods | +| `runtime.md` | Custom Subtensor runtime calls methods | + +## Project structure + +``` +subtensor-docs-gen/ +β”œβ”€β”€ package.json +β”œβ”€β”€ tsconfig.json +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ generate.ts ← main entry point +β”‚ β”œβ”€β”€ types.ts ← Bittensor custom SCALE types + RPC defs +β”‚ β”œβ”€β”€ utils.ts ← shared Markdown helpers +β”‚ └── generators/ +β”‚ β”œβ”€β”€ extrinsics.ts +β”‚ β”œβ”€β”€ events.ts +β”‚ β”œβ”€β”€ errors.ts +β”‚ β”œβ”€β”€ storage.ts +β”‚ β”œβ”€β”€ constants.ts +β”‚ β”œβ”€β”€ rpc.ts +β”‚ β”œβ”€β”€ runtime.ts +``` + +## How it works + +1. `generate.ts` connects to the node via WebSocket using `@polkadot/api`. +2. The API performs a `state_getMetadata` RPC call, returning SCALE-encoded FRAME metadata. +3. Custom types from `types.ts` are registered so Bittensor-specific structs decode correctly. +4. Each generator module walks its namespace (`api.tx`, `api.events`, etc.), extracts doc comments and type info, and writes a Markdown file. +5. `constants.ts` additionally reads the _live values_ of each constant from the running node. +6. All files are written to the configured `OUTPUT_DIR`. + +## Automation + +A GitHub Actions workflow at `.github/workflows/update-api-docs.yml` runs every Friday at 06:00 UTC. +It opens a pull request with the updated docs when changes are detected. + +You can also trigger it manually from the **Actions** tab with an optional custom endpoint. + +## Adding new custom types + +If Subtensor introduces a new pallet or struct, update `src/types.ts`: + +```typescript +export const bittensorTypes = { + // existing types... + MyNewStruct: { + field_one: "u64", + field_two: "AccountId", + }, +}; +``` diff --git a/src/subtensor-docs-gen/package.json b/src/subtensor-docs-gen/package.json new file mode 100644 index 00000000..35f418c0 --- /dev/null +++ b/src/subtensor-docs-gen/package.json @@ -0,0 +1,22 @@ +{ + "name": "subtensor-docs-gen", + "version": "1.0.0", + "description": "Generates Markdown API reference docs from Subtensor runtime metadata via @polkadot/api", + "scripts": { + "generate": "ts-node src/generate.ts", + "generate:local": "SUBTENSOR_WS=ws://127.0.0.1:9944 ts-node src/generate.ts", + "generate:test": "SUBTENSOR_WS=wss://test.finney.opentensor.ai:443 ts-node src/generate.ts", + "lint": "tsc --noEmit" + }, + "dependencies": { + "@polkadot/api": "^10.11.1", + "@polkadot/types": "^10.11.1", + "@polkadot/util": "^12.6.1" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.3.0" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" +} diff --git a/src/subtensor-docs-gen/src/generate.ts b/src/subtensor-docs-gen/src/generate.ts new file mode 100644 index 00000000..8f2b6bcb --- /dev/null +++ b/src/subtensor-docs-gen/src/generate.ts @@ -0,0 +1,91 @@ +/** + * generate.ts + * + * Main entry point for the Subtensor API reference doc generator. + * + * Usage: + * yarn generate # mainnet (finney) + * yarn generate:local # local dev node + * yarn generate:test # testnet + * SUBTENSOR_WS=ws://... yarn ts-node src/generate.ts + * + * Output: + * Writes Markdown files to OUTPUT_DIR (default: ../docs/api-reference + * relative to the subtensor repo root, i.e. one level up from this project). + */ + +import * as path from "path"; +import { ApiPromise, WsProvider } from "@polkadot/api"; +import { bittensorTypes, bittensorRpc } from "./types"; +import { ensureDir } from "./utils"; +import { generateExtrinsics } from "./generators/extrinsics"; +import { generateEvents } from "./generators/events"; +import { generateErrors } from "./generators/errors"; +import { generateStorage } from "./generators/storage"; +import { generateConstants } from "./generators/constants"; +import { generateRpc } from "./generators/rpc"; +import { generateRuntimeCalls } from "./generators/runtime"; + +// ── Configuration ───────────────────────────────────────────────────────────── + +const WS_ENDPOINT = + process.env.SUBTENSOR_WS ?? "wss://entrypoint-finney.opentensor.ai:443"; + +const OUTPUT_DIR = + process.env.OUTPUT_DIR ?? + path.resolve(__dirname, "..", "..", "..", "docs", "subtensor-api"); + +// ── Main ────────────────────────────────────────────────────────────────────── + +async function main(): Promise { + console.log("\n╔════════════════════════════════════════════════╗"); + console.log("β•‘ Subtensor API Reference β€” Doc Generator β•‘"); + console.log("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n"); + console.log(` Endpoint : ${WS_ENDPOINT}`); + console.log(` Output : ${OUTPUT_DIR}\n`); + + // ── Connect ────────────────────────────────────────────────────────────── + console.log("β–Ά Connecting to node..."); + const provider = new WsProvider(WS_ENDPOINT, 5_000 /* reconnect delay ms */); + + const api = await ApiPromise.create({ + provider, + types: bittensorTypes, + rpc: bittensorRpc, + }); + + await api.isReady; + + const chain = (await api.rpc.system.chain()).toString(); + const nodeName = (await api.rpc.system.name()).toString(); + const nodeVer = (await api.rpc.system.version()).toString(); + const specVer = api.runtimeVersion.specVersion.toString(); + + console.log(`\n Chain : ${chain}`); + console.log(` Node : ${nodeName} ${nodeVer}`); + console.log(` Spec ver : ${specVer}`); + + // ── Prepare output directory ────────────────────────────────────────────── + ensureDir(OUTPUT_DIR); + console.log("\nβ–Ά Generating docs...\n"); + + // ── Run all generators ──────────────────────────────────────────────────── + generateExtrinsics(api, OUTPUT_DIR); + generateEvents(api, OUTPUT_DIR); + generateErrors(api, OUTPUT_DIR); + generateStorage(api, OUTPUT_DIR); + generateConstants(api, OUTPUT_DIR); + await generateRpc(api, OUTPUT_DIR); + generateRuntimeCalls(api, OUTPUT_DIR); + + console.log("\nβ–Ά Done.\n"); + + // ── Clean up ────────────────────────────────────────────────────────────── + await api.disconnect(); + process.exit(0); +} + +main().catch((err) => { + console.error("\nβœ– Fatal error:", err); + process.exit(1); +}); diff --git a/src/subtensor-docs-gen/src/generators/constants.ts b/src/subtensor-docs-gen/src/generators/constants.ts new file mode 100644 index 00000000..529c678d --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/constants.ts @@ -0,0 +1,57 @@ +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { + extractDocs, + resolveTypeById, + fileHeader, + writeFile, + sortedPallets, + palletAnchor, +} from "../utils"; + +export function generateConstants(api: ApiPromise, outputDir: string): void { + const lines: string[] = []; + + lines.push( + fileHeader( + "Constants", + "This page contains compile-time runtime constants for the Bittensor (Subtensor) runtime. " + + "Accessible via `api.consts..`. Values read live from node.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + const pallets = sortedPallets(api.consts as any); + for (const [n] of pallets) lines.push(`- **[${n}](#${palletAnchor(n)})**`); + + for (const [palletName, palletConsts] of pallets) { + lines.push(`\n## \`${palletName}\`\n`); + const consts = Object.entries(palletConsts as Record).sort( + ([a], [b]) => a.localeCompare(b), + ); + + for (const [constName, constVal] of consts) { + const meta = (constVal as any)?.meta; + const typeStr = ( + meta?.type !== undefined + ? resolveTypeById(meta.type, api.registry) + : "unknown" + ) + .replace(/\s+/g, " ") + .trim(); + + lines.push(`### \`${constName}\`: \`${typeStr}\`\n`); + lines.push(`- **interface**: \`api.consts.${palletName}.${constName}\``); + try { + lines.push(`- **value**: \`${(constVal as any).toString()}\``); + } catch { + lines.push(`- **value**: *(unable to decode)*`); + } + const docs = extractDocs(meta?.docs ?? []); + if (docs) lines.push(`- **summary**: ${docs}`); + lines.push(""); + } + } + + writeFile(path.join(outputDir, "constants.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/generators/errors.ts b/src/subtensor-docs-gen/src/generators/errors.ts new file mode 100644 index 00000000..90c8b425 --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/errors.ts @@ -0,0 +1,45 @@ +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { + extractDocs, + fileHeader, + writeFile, + sortedPallets, + palletAnchor, +} from "../utils"; + +export function generateErrors(api: ApiPromise, outputDir: string): void { + const lines: string[] = []; + + lines.push( + fileHeader( + "Errors", + "This page contains error variants returned by the Bittensor (Subtensor) runtime. " + + "Accessible via `api.errors..`.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + const pallets = sortedPallets(api.errors as any); + for (const [n] of pallets) lines.push(`- **[${n}](#${palletAnchor(n)})**`); + + for (const [palletName, palletErrors] of pallets) { + lines.push(`\n## \`${palletName}\`\n`); + const errors = Object.entries(palletErrors as Record).sort( + ([a], [b]) => a.localeCompare(b), + ); + + for (const [errorName, errorDef] of errors) { + const meta = errorDef?.meta; + if (!meta) continue; + + lines.push(`### \`${errorName}\`\n`); + lines.push(`- **interface**: \`api.errors.${palletName}.${errorName}\``); + const docs = extractDocs(meta.docs ?? []); + if (docs) lines.push(`- **summary**: ${docs}`); + lines.push(""); + } + } + + writeFile(path.join(outputDir, "errors.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/generators/events.ts b/src/subtensor-docs-gen/src/generators/events.ts new file mode 100644 index 00000000..318ff8a4 --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/events.ts @@ -0,0 +1,51 @@ +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { + extractDocs, + resolveTypeForHeading, + fileHeader, + writeFile, + sortedPallets, + palletAnchor, +} from "../utils"; + +export function generateEvents(api: ApiPromise, outputDir: string): void { + const lines: string[] = []; + + lines.push( + fileHeader( + "Events", + "The following page contains runtime events emitted by the Bittensor (Subtensor) runtime. " + + "Accessible via `api.events..`.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + const pallets = sortedPallets(api.events as any); + for (const [n] of pallets) lines.push(`- **[${n}](#${palletAnchor(n)})**`); + + for (const [palletName, palletEvents] of pallets) { + lines.push(`\n## \`${palletName}\`\n`); + const events = Object.entries(palletEvents as Record).sort( + ([a], [b]) => a.localeCompare(b), + ); + + for (const [eventName, eventDef] of events) { + const meta = eventDef?.meta; + if (!meta) continue; + + // Types only, single-line β€” use resolveTypeForHeading to avoid line breaks + const types = (meta.fields ?? []) + .map((f: any) => resolveTypeForHeading(f, api.registry)) + .join(", "); + + lines.push(`### \`${eventName}(${types})\`\n`); + lines.push(`- **interface**: \`api.events.${palletName}.${eventName}\``); + const docs = extractDocs(meta.docs ?? []); + if (docs) lines.push(`- **summary**: ${docs}`); + lines.push(""); + } + } + + writeFile(path.join(outputDir, "events.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/generators/extrinsics.ts b/src/subtensor-docs-gen/src/generators/extrinsics.ts new file mode 100644 index 00000000..718d32ab --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/extrinsics.ts @@ -0,0 +1,67 @@ +/** + * generators/extrinsics.ts + */ + +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { + extractDocs, + resolveTypeForHeading, + fileHeader, + writeFile, + sortedPallets, + palletAnchor, +} from "../utils"; + +export function generateExtrinsics(api: ApiPromise, outputDir: string): void { + const lines: string[] = []; + + lines.push( + fileHeader( + "Extrinsics", + "The following sections contain Extrinsic methods that are part of the Bittensor " + + "(Subtensor) runtime. On the API, these are exposed via `api.tx..`.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + const pallets = sortedPallets(api.tx as any); + for (const [n] of pallets) lines.push(`- **[${n}](#${palletAnchor(n)})**`); + + for (const [palletName, palletTx] of pallets) { + // Backtick-wrapped pallet name, no horizontal rule + lines.push(`\n## \`${palletName}\`\n`); + + const calls = Object.entries(palletTx as Record).sort( + ([a], [b]) => a.localeCompare(b), + ); + + for (const [callName, callFn] of calls) { + const meta = callFn?.meta; + if (!meta) continue; + + // Build signature β€” use resolveTypeForHeading to prevent multi-line types + const params = (meta.fields ?? []).map((field: any) => { + const name = field.name?.toString?.() ?? "_"; + const typeStr = resolveTypeForHeading(field, api.registry); + return `${name}: ${typeStr}`; + }); + // Collapse everything onto one line β€” heading MUST be single-line + const signature = `${callName}(${params.join(", ")})` + .replace(/\s+/g, " ") + .trim(); + + lines.push(`### \`${signature}\`\n`); + lines.push(`- **interface**: \`api.tx.${palletName}.${callName}\``); + + const docs = extractDocs(meta.docs ?? []); + if (docs) { + lines.push(`- **summary**: ${docs}`); + } + + lines.push(""); + } + } + + writeFile(path.join(outputDir, "extrinsics.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/generators/rpc.ts b/src/subtensor-docs-gen/src/generators/rpc.ts new file mode 100644 index 00000000..6e433523 --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/rpc.ts @@ -0,0 +1,223 @@ +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { rpcDefinitions } from "@polkadot/types"; +import { fileHeader, writeFile, palletAnchor } from "../utils"; + +// ── Types ─────────────────────────────────────────────────────────────────── + +type MethodInfo = { + name: string; + description: string; + params: { name: string; type: string; isOptional?: boolean }[]; + returnType: string; + isSubscription: boolean; + isUnsafe: boolean; +}; + +// ── Helpers ───────────────────────────────────────────────────────────────── + +/** + * Determines the real namespace for a method. + * Some methods use `aliasSection` (e.g. `net_*` and `web3_*` are defined + * inside the `eth` interface module but belong to their own namespace). + */ +function effectiveSection(section: string, def: any): string { + return def.aliasSection?.toString?.() ?? section; +} + +/** + * Queries the node for its supported RPC methods via `rpc_methods`. + * Returns a Set of "section_method" strings, or null if the call fails. + */ +async function fetchNodeMethods(api: ApiPromise): Promise | null> { + try { + const result = await (api.rpc as any).rpc.methods(); + const list: string[] = + result?.methods?.toJSON?.() ?? result?.toJSON?.()?.methods ?? []; + if (list.length === 0) return null; + return new Set(list); + } catch { + return null; + } +} + +/** + * Builds the JSON-RPC method name(s) for matching against the node's method list. + * Handles aliases: e.g. `chain.getFinalizedHead` also registers as + * `chain_getFinalisedHead`. + */ +function rpcMethodNames(section: string, method: string, def: any): string[] { + const names = [`${section}_${method}`]; + const aliases: string[] = def?.alias?.toJSON?.() ?? def?.alias ?? []; + if (Array.isArray(aliases)) { + for (const a of aliases) names.push(a); + } + // pubsub methods also register subscribe/unsubscribe variants + if (Array.isArray(def?.pubsub) && def.pubsub.length >= 2) { + names.push(`${section}_${def.pubsub[1]}`); + } + return names; +} + +// ── Generator ───────────────────────────────────────────────────────────────── + +export async function generateRpc( + api: ApiPromise, + outputDir: string, +): Promise { + const lines: string[] = []; + + lines.push( + fileHeader( + "RPC", + "This page shows JSON-RPC methods available on a Bittensor (Subtensor) node. " + + "Accessible via `api.rpc..`.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + // ── 1. Ask the node which methods it actually supports ──────────────────── + + const nodeMethods = await fetchNodeMethods(api); + if (nodeMethods) { + console.log( + ` β„Ή rpc_methods returned ${nodeMethods.size} methods β€” filtering to node-supported RPCs`, + ); + } else { + console.log( + " ⚠ rpc_methods unavailable β€” including all known definitions", + ); + } + + // ── 2. Build definitions catalogue from @polkadot/types ─────────────────── + + const allNamespaces = new Map< + string, + Map + >(); + + for (const [section, defs] of Object.entries( + rpcDefinitions as Record>, + )) { + for (const [method, def] of Object.entries(defs)) { + const ns = effectiveSection(section, def); + + if (!allNamespaces.has(ns)) allNamespaces.set(ns, new Map()); + const nsMethods = allNamespaces.get(ns)!; + + if (!nsMethods.has(method)) { + nsMethods.set(method, { + info: { + name: method, + description: def.description?.toString?.() ?? "", + params: (def.params ?? []).map((p: any) => ({ + name: p.name?.toString?.() ?? "_", + type: p.type?.toString?.() ?? "unknown", + isOptional: !!p.isOptional, + })), + returnType: def.type?.toString?.() ?? "unknown", + isSubscription: Array.isArray(def.pubsub), + isUnsafe: !!def.isUnsafe, + }, + def, + }); + } + } + } + + // ── 3. Runtime supplement β€” pick up anything extra the node exposes ──────── + + try { + const rpcCore = api.rpc as any; + const mapping: Map | undefined = rpcCore?.mapping; + if (mapping) { + for (const [, def] of mapping) { + const section: string = def.section ?? ""; + const method: string = def.method ?? ""; + if (!section || !method) continue; + + const ns = effectiveSection(section, def); + if (!allNamespaces.has(ns)) allNamespaces.set(ns, new Map()); + const nsMethods = allNamespaces.get(ns)!; + + if (!nsMethods.has(method)) { + nsMethods.set(method, { + info: { + name: method, + description: def.description?.toString?.() ?? "", + params: (def.params ?? []).map((p: any) => ({ + name: p.name?.toString?.() ?? "_", + type: p.type?.toString?.() ?? "unknown", + isOptional: !!p.isOptional, + })), + returnType: def.type?.toString?.() ?? "unknown", + isSubscription: !!def.isSubscription, + isUnsafe: !!def.isUnsafe, + }, + def, + }); + } + } + } + } catch { + /* non-critical β€” we already have the static definitions */ + } + + // ── 4. Filter to only node-supported methods (if available) ─────────────── + + const namespaces = new Map(); + + for (const [ns, methods] of allNamespaces) { + const filtered: MethodInfo[] = []; + + for (const [method, { info, def }] of methods) { + if (nodeMethods) { + const names = rpcMethodNames(ns, method, def); + const supported = names.some((n) => nodeMethods.has(n)); + if (!supported) continue; + } + filtered.push(info); + } + + if (filtered.length > 0) { + filtered.sort((a, b) => a.name.localeCompare(b.name)); + namespaces.set(ns, filtered); + } + } + + // ── Sort ────────────────────────────────────────────────────────────────── + + const sortedNs = [...namespaces.entries()].sort(([a], [b]) => + a.localeCompare(b), + ); + + // ── Table of contents ───────────────────────────────────────────────────── + + for (const [n] of sortedNs) lines.push(`- **[${n}](#${palletAnchor(n)})**`); + + // ── Per-namespace sections ──────────────────────────────────────────────── + + for (const [nsName, methods] of sortedNs) { + lines.push(`\n## \`${nsName}\`\n`); + + for (const m of methods) { + const paramStr = m.params + .map((p) => `${p.name}${p.isOptional ? "?" : ""}: ${p.type}`) + .join(", "); + + lines.push(`### \`${m.name}(${paramStr})\`: \`${m.returnType}\`\n`); + lines.push(`- **interface**: \`api.rpc.${nsName}.${m.name}\``); + if (m.isSubscription) lines.push(`- **jsonrpc**: subscription`); + if (m.isUnsafe) + lines.push(`- **unsafe**: this method is flagged as unsafe`); + if (m.description) lines.push(`- **summary**: ${m.description}`); + lines.push(""); + } + } + + if (sortedNs.length === 0) { + lines.push("\n*No RPC methods found.*\n"); + } + + writeFile(path.join(outputDir, "rpc.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/generators/runtime.ts b/src/subtensor-docs-gen/src/generators/runtime.ts new file mode 100644 index 00000000..88872e9f --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/runtime.ts @@ -0,0 +1,177 @@ +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { extractDocs, resolveTypeById, fileHeader, writeFile } from "../utils"; + +/** + * Attempt to derive a readable type name from a type ID via the registry. + */ +function readableType(typeId: any, registry: any): string { + if (typeId === undefined || typeId === null) return "unknown"; + try { + return resolveTypeById(typeId, registry); + } catch { + return String(typeId); + } +} + +export function generateRuntimeCalls(api: ApiPromise, outputDir: string): void { + const lines: string[] = []; + + lines.push( + fileHeader( + "Runtime Calls", + "This page includes runtime API calls exposed by the Bittensor (Subtensor) runtime. " + + "Accessible via `api.call..`.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + // ── Collect runtime APIs from metadata ──────────────────────────────────── + + type ApiEntry = { + apiName: string; + methods: { + methodName: string; + params: string; + returnType: string; + docs: string; + }[]; + }; + + const apiEntries: ApiEntry[] = []; + + try { + const runtimeApis = (api as any).runtimeMetadata?.asLatest?.apis; + if (runtimeApis) { + for (const runtimeApi of runtimeApis) { + const traitNameParts: string[] = + runtimeApi.name?.toJSON?.() ?? + runtimeApi.name?.toString?.()?.split("::") ?? + []; + const apiName = + traitNameParts.length > 0 + ? traitNameParts[traitNameParts.length - 1] + : (runtimeApi.name?.toString?.() ?? "Unknown"); + + const methods: ApiEntry["methods"] = []; + + for (const method of runtimeApi.methods ?? []) { + const methodName = method.name?.toString?.() ?? "unknown"; + + const params = (method.inputs ?? []) + .map((input: any) => { + const name = input.name?.toString?.() ?? "_"; + const typeStr = readableType( + input.type ?? input.ty, + api.registry, + ); + return `${name}: ${typeStr}`; + }) + .join(", "); + + const returnType = readableType( + method.output ?? method.outputTy, + api.registry, + ); + + const docs = extractDocs( + method.docs?.toJSON?.() ?? method.docs ?? [], + ); + + methods.push({ methodName, params, returnType, docs }); + } + + if (methods.length > 0) { + methods.sort((a, b) => a.methodName.localeCompare(b.methodName)); + apiEntries.push({ apiName, methods }); + } + } + } + } catch (err) { + console.warn(" ⚠ Could not read runtime API metadata:", err); + } + + // ── Fallback: introspect api.call namespace ─────────────────────────────── + + if (apiEntries.length === 0) { + try { + const callNs = api.call as any; + if (callNs && typeof callNs === "object") { + for (const [apiName, apiMethods] of Object.entries(callNs).sort( + ([a], [b]) => a.localeCompare(b), + )) { + if ( + !apiMethods || + typeof apiMethods !== "object" || + apiName.startsWith("_") + ) + continue; + + const methods: ApiEntry["methods"] = []; + + for (const [methodName, methodFn] of Object.entries( + apiMethods as Record, + ).sort(([a], [b]) => a.localeCompare(b))) { + if (typeof methodFn !== "function") continue; + + const meta = (methodFn as any)?.meta; + const params = (meta?.fields ?? meta?.params ?? []) + .map((f: any) => { + const name = f.name?.toString?.() ?? "_"; + const typeStr = readableType( + f.type ?? f.typeName, + api.registry, + ); + return `${name}: ${typeStr}`; + }) + .join(", "); + + const returnType = meta?.type + ? readableType(meta.type, api.registry) + : "unknown"; + + const docs = extractDocs(meta?.docs ?? []); + methods.push({ methodName, params, returnType, docs }); + } + + if (methods.length > 0) { + apiEntries.push({ apiName, methods }); + } + } + } + } catch (err) { + console.warn(" ⚠ Could not introspect api.call namespace:", err); + } + } + + apiEntries.sort((a, b) => a.apiName.localeCompare(b.apiName)); + + // ── Table of contents ───────────────────────────────────────────────────── + for (const entry of apiEntries) { + lines.push(`- **[${entry.apiName}](#${entry.apiName.toLowerCase()})**`); + } + + // ── Per-API sections ────────────────────────────────────────────────────── + for (const { apiName, methods } of apiEntries) { + lines.push(`\n## \`${apiName}\`\n`); + + for (const { methodName, params, returnType, docs } of methods) { + const signature = `${methodName}(${params})`.replace(/\s+/g, " ").trim(); + + lines.push(`### \`${signature}\`: \`${returnType}\`\n`); + lines.push(`- **interface**: \`api.call.${apiName}.${methodName}\``); + if (docs) { + lines.push(`- **summary**: ${docs}`); + } + lines.push(""); + } + } + + if (apiEntries.length === 0) { + lines.push( + "\n*No runtime API calls found in the connected node's metadata.*\n", + ); + } + + writeFile(path.join(outputDir, "runtime.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/generators/storage.ts b/src/subtensor-docs-gen/src/generators/storage.ts new file mode 100644 index 00000000..863b27f6 --- /dev/null +++ b/src/subtensor-docs-gen/src/generators/storage.ts @@ -0,0 +1,243 @@ +import * as path from "path"; +import type { ApiPromise } from "@polkadot/api"; +import { + extractDocs, + fileHeader, + writeFile, + sortedPallets, + palletAnchor, +} from "../utils"; + +// ── Type name helpers ───────────────────────────────────────────────────────── + +/** + * Converts snake_case to PascalCase. + */ +function snakeToPascal(s: string): string { + return s + .replace(/_([a-z])/g, (_, c) => c.toUpperCase()) + .replace(/^[a-z]/, (c) => c.toUpperCase()); +} + +/** + * Split "A,B,E" on top-level commas only (respects nesting). + */ +function splitTypeList(s: string): string[] { + const parts: string[] = []; + let depth = 0, + start = 0; + for (let i = 0; i < s.length; i++) { + if (s[i] === "<" || s[i] === "(") depth++; + else if (s[i] === ">" || s[i] === ")") depth--; + else if (s[i] === "," && depth === 0) { + parts.push(s.slice(start, i)); + start = i + 1; + } + } + parts.push(s.slice(start)); + return parts; +} + +/** + * Simplify a raw TypeDef type string, replacing verbose generics with + * readable equivalents: + * BoundedVec β†’ Vec + * WeakBoundedVec β†’ Vec + * BoundedBTreeMap β†’ BTreeMap + * BoundedBTreeSet β†’ BTreeSet + * WrapperOpaque β†’ T + * WrapperKeepOpaque β†’ T + * Box β†’ T + */ +function simplifyTypeString(t: string): string { + // BoundedVec / WeakBoundedVec β†’ Vec + t = t.replace( + /(?:Weak)?BoundedVec<(.+),\s*[^,>]+>/, + (_m, inner) => `Vec<${inner.trim()}>`, + ); + // BoundedBTreeMap β†’ BTreeMap + t = t.replace( + /BoundedBTreeMap<(.+),\s*[^,>]+>/, + (_m, kv) => `BTreeMap<${kv.trim()}>`, + ); + // BoundedBTreeSet β†’ BTreeSet + t = t.replace( + /BoundedBTreeSet<(.+),\s*[^,>]+>/, + (_m, inner) => `BTreeSet<${inner.trim()}>`, + ); + // WrapperOpaque / WrapperKeepOpaque / Box β†’ unwrap + t = t.replace(/(?:WrapperKeepOpaque|WrapperOpaque|Box)<(.+)>/, (_m, inner) => + inner.trim(), + ); + return t.trim(); +} + +/** + * Derives a clean, readable type name from a PortableRegistry lookup ID. + * + * Priority: + * 1. Named path from SiType (e.g. AccountData, Multisig) β€” most specific + * 2. TypeDef string, simplified (BoundedVecβ†’Vec, etc.) + * 3. Fallback to string of the id + */ +function readableTypeName(id: any, registry: any): string { + if (!registry || id === undefined) return String(id ?? "?"); + + // Try named path first + try { + const siType = registry.lookup.getSiType(id); + if (siType) { + const pathArr: string[] = siType.path?.toJSON?.() ?? []; + if (pathArr.length > 0) { + const leaf = pathArr[pathArr.length - 1]; + // Skip generic Rust wrapper names that aren't meaningful on their own + const SKIP = new Set([ + "BoundedVec", + "WeakBoundedVec", + "BoundedBTreeMap", + "BoundedBTreeSet", + "WrapperOpaque", + "WrapperKeepOpaque", + "Box", + "Vec", + ]); + if (!SKIP.has(leaf)) { + return snakeToPascal(leaf); + } + } + } + } catch { + /* fall through */ + } + + // TypeDef string, simplified + try { + const def = registry.lookup.getTypeDef(id); + if (def?.type) { + const t = def.type.trim(); + if (t.startsWith("{")) { + // Inline struct JSON β€” try to get name from path instead + try { + const siType = registry.lookup.getSiType(id); + const pathArr: string[] = siType?.path?.toJSON?.() ?? []; + if (pathArr.length > 0) + return snakeToPascal(pathArr[pathArr.length - 1]); + } catch { + /* fall through */ + } + return "Object"; + } + return simplifyTypeString(t); + } + } catch { + /* fall through */ + } + + return String(id); +} + +/** + * Resolves a key ID to a list of type strings. + * For tuple keys "(A,B,C)", returns ["A","B","C"]. + * For a single key, returns ["KeyType"]. + */ +function readableKeyTypes(id: any, registry: any): string[] { + if (!registry || id === undefined) return [String(id ?? "?")]; + try { + const def = registry.lookup.getTypeDef(id); + if (def?.type) { + const t = def.type.trim(); + if (t.startsWith("(") && t.endsWith(")")) { + const inner = t.slice(1, -1); + return splitTypeList(inner).map((p) => simplifyTypeString(p.trim())); + } + } + } catch { + /* fall through */ + } + return [readableTypeName(id, registry)]; +} + +// ── Storage heading ─────────────────────────────────────────────────────────── + +/** + * Builds the heading in the form: + * `name(KeyType)`: `ValueType` β€” Maps + * `name(Key1, Key2)`: `ValueType` β€” DoubleMaps / tuple keys + * `name`: `ValueType` β€” Plain values + */ +function storageHeading(queryName: string, meta: any, registry: any): string { + if (meta.type?.isMap) { + const m = meta.type.asMap; + const keys = readableKeyTypes(m.key, registry).join(", "); + const value = readableTypeName(m.value, registry); + return `\`${queryName}(${keys})\`: \`${value}\``; + } + + if (meta.type?.isDoubleMap) { + const dm = meta.type.asDoubleMap; + const k1 = readableTypeName(dm.key1, registry); + const k2 = readableTypeName(dm.key2, registry); + const value = readableTypeName(dm.value, registry); + return `\`${queryName}(${k1}, ${k2})\`: \`${value}\``; + } + + if (meta.type?.isNMap) { + const nm = meta.type.asNMap; + const keys = (nm.keyVec ?? []) + .map((k: any) => readableTypeName(k, registry)) + .join(", "); + const value = readableTypeName(nm.value, registry); + return `\`${queryName}(${keys})\`: \`${value}\``; + } + + if (meta.type?.isPlain) { + const value = readableTypeName(meta.type.asPlain, registry); + return `\`${queryName}\`: \`${value}\``; + } + + return `\`${queryName}\``; +} + +// ── Generator ───────────────────────────────────────────────────────────────── + +export function generateStorage(api: ApiPromise, outputDir: string): void { + const lines: string[] = []; + + lines.push( + fileHeader( + "Storage", + "This page contains storage query definitions for the Bittensor (Subtensor) runtime. " + + "Accessible via `api.query..`.", + (api as any)._options?.provider?.endpoint ?? "subtensor node", + ), + ); + + const pallets = sortedPallets(api.query as any); + for (const [n] of pallets) lines.push(`- **[${n}](#${palletAnchor(n)})**`); + + for (const [palletName, palletQuery] of pallets) { + lines.push(`\n## \`${palletName}\`\n`); + const items = Object.entries(palletQuery as Record).sort( + ([a], [b]) => a.localeCompare(b), + ); + + for (const [queryName, queryFn] of items) { + const meta = queryFn?.creator?.meta ?? queryFn?.meta; + if (!meta) continue; + + lines.push(`### ${storageHeading(queryName, meta, api.registry)}\n`); + lines.push(`- **interface**: \`api.query.${palletName}.${queryName}\``); + + const modifier = meta.modifier?.toString?.(); + if (modifier && modifier !== "Default") + lines.push(`- **modifier**: \`${modifier}\``); + + const docs = extractDocs(meta.docs ?? []); + if (docs) lines.push(`- **summary**: ${docs}`); + lines.push(""); + } + } + + writeFile(path.join(outputDir, "storage.md"), lines.join("\n")); +} diff --git a/src/subtensor-docs-gen/src/types.ts b/src/subtensor-docs-gen/src/types.ts new file mode 100644 index 00000000..69ae5103 --- /dev/null +++ b/src/subtensor-docs-gen/src/types.ts @@ -0,0 +1,355 @@ +/** + * types.ts + * + * Custom type definitions for the Bittensor (Subtensor) runtime. + * These must be registered with @polkadot/api before connecting so that + * storage items and RPC return values using Bittensor-specific structs + * can be decoded correctly. + * + * Source of truth: pallets/subtensor/src/rpc.rs and pallets/subtensor/src/lib.rs + * in opentensor/subtensor. + */ + +export const bittensorTypes = { + // ── Neuron endpoint info ────────────────────────────────────────────────── + + AxonInfo: { + block: 'u64', + version: 'u32', + ip: 'u128', + port: 'u16', + ip_type: 'u8', + protocol: 'u8', + placeholder1: 'u8', + placeholder2: 'u8', + }, + + PrometheusInfo: { + block: 'u64', + version: 'u32', + ip: 'u128', + port: 'u16', + ip_type: 'u8', + }, + + // ── Full neuron snapshot (returned by RPC calls) ────────────────────────── + + NeuronInfo: { + hotkey: 'AccountId', + coldkey: 'AccountId', + uid: 'Compact', + netuid: 'Compact', + active: 'bool', + axon_info: 'AxonInfo', + prometheus_info: 'PrometheusInfo', + stake: 'Vec<(AccountId, Compact)>', + rank: 'Compact', + emission: 'Compact', + incentive: 'Compact', + consensus: 'Compact', + trust: 'Compact', + validator_trust: 'Compact', + dividends: 'Compact', + last_update: 'Compact', + validator_permit: 'bool', + weights: 'Vec<(Compact, Compact)>', + bonds: 'Vec<(Compact, Compact)>', + pruning_score: 'Compact', + }, + + NeuronInfoLite: { + hotkey: 'AccountId', + coldkey: 'AccountId', + uid: 'Compact', + netuid: 'Compact', + active: 'bool', + axon_info: 'AxonInfo', + prometheus_info: 'PrometheusInfo', + stake: 'Vec<(AccountId, Compact)>', + rank: 'Compact', + emission: 'Compact', + incentive: 'Compact', + consensus: 'Compact', + trust: 'Compact', + validator_trust: 'Compact', + dividends: 'Compact', + last_update: 'Compact', + validator_permit: 'bool', + pruning_score: 'Compact', + }, + + // ── Subnet info ─────────────────────────────────────────────────────────── + + SubnetInfo: { + netuid: 'Compact', + rho: 'Compact', + kappa: 'Compact', + difficulty: 'Compact', + immunity_period: 'Compact', + max_allowed_validators: 'Compact', + min_allowed_weights: 'Compact', + max_weights_limit: 'Compact', + scaling_law_power: 'Compact', + subnetwork_n: 'Compact', + max_allowed_uids: 'Compact', + blocks_since_last_step: 'Compact', + tempo: 'Compact', + network_modality: 'Compact', + network_connect: 'Vec<[u16; 2]>', + emission_values: 'Compact', + burn: 'Compact', + owner: 'AccountId', + owner_cut: 'Compact', + activity_cutoff: 'Compact', + registration_allowed: 'bool', + target_regs_per_interval: 'Compact', + min_burn: 'Compact', + max_burn: 'Compact', + bonds_moving_avg: 'Compact', + max_regs_per_block: 'Compact', + serving_rate_limit: 'Compact', + cr_enabled: 'bool', + liquid_alpha_enabled: 'bool', + alpha_high: 'Compact', + alpha_low: 'Compact', + }, + + SubnetHyperparams: { + rho: 'Compact', + kappa: 'Compact', + immunity_period: 'Compact', + min_allowed_weights: 'Compact', + max_weights_limit: 'Compact', + tempo: 'Compact', + min_difficulty: 'Compact', + max_difficulty: 'Compact', + weights_version: 'Compact', + weights_rate_limit: 'Compact', + adjustment_interval: 'Compact', + activity_cutoff: 'Compact', + registration_allowed: 'bool', + target_regs_per_interval: 'Compact', + min_burn: 'Compact', + max_burn: 'Compact', + bonds_moving_avg: 'Compact', + max_regs_per_block: 'Compact', + serving_rate_limit: 'Compact', + max_validators: 'Compact', + adjustment_alpha: 'Compact', + difficulty: 'Compact', + commit_reveal_weights_interval: 'Compact', + commit_reveal_weights_enabled: 'bool', + alpha_high: 'Compact', + alpha_low: 'Compact', + liquid_alpha_enabled: 'bool', + }, + + // ── Delegation ──────────────────────────────────────────────────────────── + + DelegateInfo: { + delegate_ss58: 'AccountId', + take: 'Compact', + nominators: 'Vec<(AccountId, Compact)>', + owner_ss58: 'AccountId', + registrations: 'Vec>', + validator_permits: 'Vec>', + return_per_1000: 'Compact', + total_daily_return: 'Compact', + }, + + DelegateInfoLite: { + delegate_ss58: 'AccountId', + owner_ss58: 'AccountId', + take: 'Compact', + total_stake: 'Compact', + registrations: 'Vec>', + validator_permits: 'Vec>', + return_per_1000: 'Compact', + total_daily_return: 'Compact', + }, + + // ── Stake info ──────────────────────────────────────────────────────────── + + StakeInfo: { + hotkey: 'AccountId', + coldkey: 'AccountId', + stake: 'Compact', + locked: 'Compact', + emission: 'Compact', + drain: 'Compact', + is_registered: 'bool', + }, + + // ── Commitment ──────────────────────────────────────────────────────────── + + CommitmentInfo: { + fields: 'Vec', + }, + + CommitmentField: { + _enum: { + None: null, + Raw0: '[u8; 0]', + Raw1: '[u8; 1]', + Raw2: '[u8; 2]', + Raw3: '[u8; 3]', + Raw4: '[u8; 4]', + Raw5: '[u8; 5]', + Raw6: '[u8; 6]', + Raw7: '[u8; 7]', + Raw8: '[u8; 8]', + Raw9: '[u8; 9]', + Raw10: '[u8; 10]', + Raw11: '[u8; 11]', + Raw12: '[u8; 12]', + Raw13: '[u8; 13]', + Raw14: '[u8; 14]', + Raw15: '[u8; 15]', + Raw16: '[u8; 16]', + Raw17: '[u8; 17]', + Raw18: '[u8; 18]', + Raw19: '[u8; 19]', + Raw20: '[u8; 20]', + Raw21: '[u8; 21]', + Raw22: '[u8; 22]', + Raw23: '[u8; 23]', + Raw24: '[u8; 24]', + Raw25: '[u8; 25]', + Raw26: '[u8; 26]', + Raw27: '[u8; 27]', + Raw28: '[u8; 28]', + Raw29: '[u8; 29]', + Raw30: '[u8; 30]', + Raw31: '[u8; 31]', + Raw32: '[u8; 32]', + BlakeTwo256: 'H256', + Sha256: 'H256', + Keccak256: 'H256', + ShaThree256: 'H256', + }, + }, + + // ── Proxy types ─────────────────────────────────────────────────────────── + + ProxyType: { + _enum: [ + 'Any', + 'Owner', + 'NonTransfer', + 'Senate', + 'NonFungibile', + 'Triumvirate', + 'Governance', + 'SmallStake', + 'BigStake', + 'Registration', + 'Transfer', + 'RootWeights', + ], + }, +}; + +/** + * Custom RPC definitions for Subtensor. + * These supplement the standard Substrate RPC set. + * Source: pallets/subtensor/src/rpc.rs and node/src/rpc.rs + */ +export const bittensorRpc = { + neuronInfo: { + getNeurons: { + description: 'Get the full NeuronInfo for all neurons on a subnet.', + params: [{ name: 'netuid', type: 'u16' }], + type: 'Vec', + }, + getNeuronsLite: { + description: 'Get lightweight NeuronInfoLite for all neurons on a subnet (no weights/bonds).', + params: [{ name: 'netuid', type: 'u16' }], + type: 'Vec', + }, + getNeuron: { + description: 'Get NeuronInfo for a single neuron by UID.', + params: [ + { name: 'netuid', type: 'u16' }, + { name: 'uid', type: 'u16' }, + ], + type: 'Option', + }, + getNeuronLite: { + description: 'Get NeuronInfoLite for a single neuron by UID.', + params: [ + { name: 'netuid', type: 'u16' }, + { name: 'uid', type: 'u16' }, + ], + type: 'Option', + }, + }, + + subnetInfo: { + getSubnetInfo: { + description: 'Get the SubnetInfo snapshot for a subnet.', + params: [{ name: 'netuid', type: 'u16' }], + type: 'Option', + }, + getSubnetInfoLite: { + description: 'Get a lightweight subnet info snapshot.', + params: [{ name: 'netuid', type: 'u16' }], + type: 'Option', + }, + getSubnetsInfo: { + description: 'Get SubnetInfo for all active subnets.', + params: [], + type: 'Vec>', + }, + getSubnetsInfoLite: { + description: 'Get lightweight info for all active subnets.', + params: [], + type: 'Vec>', + }, + getSubnetHyperparams: { + description: 'Get all hyperparameters for a subnet.', + params: [{ name: 'netuid', type: 'u16' }], + type: 'Option', + }, + }, + + delegateInfo: { + getDelegates: { + description: 'Get DelegateInfo for all registered delegates.', + params: [], + type: 'Vec', + }, + getDelegatesLite: { + description: 'Get lightweight delegate info for all delegates.', + params: [], + type: 'Vec', + }, + getDelegate: { + description: 'Get DelegateInfo for a specific hotkey.', + params: [{ name: 'hotkey_ss58', type: 'AccountId' }], + type: 'Option', + }, + getDelegateLite: { + description: 'Get lightweight delegate info for a specific hotkey.', + params: [{ name: 'hotkey_ss58', type: 'AccountId' }], + type: 'Option', + }, + getDelegated: { + description: 'Get all hotkeys that a coldkey has delegated stake to.', + params: [{ name: 'coldkey_ss58', type: 'AccountId' }], + type: 'Vec<(DelegateInfo, Compact)>', + }, + }, + + stakeInfo: { + getStakeInfoForColdkey: { + description: 'Get all StakeInfo records for a coldkey across all hotkeys.', + params: [{ name: 'coldkey_ss58', type: 'AccountId' }], + type: 'Vec', + }, + getStakeInfoForColdkeys: { + description: 'Get StakeInfo for a batch of coldkeys.', + params: [{ name: 'coldkey_ss58_vec', type: 'Vec' }], + type: 'Vec<(AccountId, Vec)>', + }, + }, +}; diff --git a/src/subtensor-docs-gen/src/utils.ts b/src/subtensor-docs-gen/src/utils.ts new file mode 100644 index 00000000..35449bf7 --- /dev/null +++ b/src/subtensor-docs-gen/src/utils.ts @@ -0,0 +1,464 @@ +/** + * utils.ts + * Shared helpers for Markdown generation. + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +// ── File I/O ────────────────────────────────────────────────────────────────── + +export function ensureDir(dir: string): void { + fs.mkdirSync(dir, { recursive: true }); +} + +export function writeFile(filePath: string, content: string): void { + fs.writeFileSync(filePath, content, 'utf8'); + const rel = path.relative(process.cwd(), filePath); + console.log(` βœ“ ${rel} (${Math.round(content.length / 1024)}KB)`); +} + +// ── Type resolution ─────────────────────────────────────────────────────────── + +const TYPE_ALIASES: Array<[RegExp, string]> = [ + [/^BlockNumberFor$/, 'u32' ], + [/^BlockNumberFor<[^>]+>$/, 'u32' ], + [/^T::BlockNumber$/, 'u32' ], + [/^T::Moment$/, 'u64' ], + [/^T::AccountId$/, 'AccountId' ], + [/^AccountIdLookupOf$/, 'MultiAddress'], + [/^AccountIdOf$/, 'AccountId' ], + [/^T::Balance$/, 'u128' ], + [/^BalanceOf$/, 'u128' ], + [/^BalanceOf]+>$/, 'u128' ], + [/^T::Hash$/, 'H256' ], + [/^T::Index$/, 'u32' ], + [/^T::Nonce$/, 'u32' ], + [/^(\w+)$/, '$1' ], +]; + +function normaliseType(raw: string): string { + const t = raw.replace(/\s+/g, ' ').trim(); + for (const [re, replacement] of TYPE_ALIASES) { + if (re.test(t)) return t.replace(re, replacement); + } + return t; +} + +function simplifyTypeForHeading(raw: string): string { + let t = raw.replace(/\s+/g, ' ').trim(); + for (const [re, replacement] of TYPE_ALIASES) { + if (re.test(t)) { t = t.replace(re, replacement); break; } + } + t = t.replace(/<<[^>]+>::\w+(?:\s+as\s+[^>]+)?>::(\w+)/g, '$1'); + t = t.replace(/]+>::(\w+)/g, '$1'); + if (t.length > 60) { + const outer = t.replace(/<.*$/s, '').trim(); + t = outer.split('::').pop() ?? outer; + } + return t; +} + +export function resolveType(field: any, registry?: any): string { + const typeName: string | undefined = field.typeName?.toString?.()?.trim(); + if (typeName && typeName.length > 0) return normaliseType(typeName); + if (registry && field.type !== undefined) { + try { + const def = registry.lookup.getTypeDef(field.type); + if (def?.type) return normaliseType(def.type); + } catch { /* fall through */ } + } + return String(field.type ?? 'unknown'); +} + +export function resolveTypeForHeading(field: any, registry?: any): string { + const typeName: string | undefined = field.typeName?.toString?.()?.trim(); + if (typeName && typeName.length > 0) return simplifyTypeForHeading(typeName); + if (registry && field.type !== undefined) { + try { + const def = registry.lookup.getTypeDef(field.type); + if (def?.type) return simplifyTypeForHeading(def.type); + } catch { /* fall through */ } + } + return String(field.type ?? 'unknown'); +} + +export function resolveTypeById(id: any, registry?: any): string { + if (!registry || id === undefined) return String(id ?? '?'); + try { + const def = registry.lookup.getTypeDef(id); + if (def?.type) return normaliseType(def.type); + } catch { /* fall through */ } + return String(id); +} + +export function fmtType(typeStr: string): string { + return `\`${typeStr}\``; +} + +// ── Rustdoc link stripping ──────────────────────────────────────────────────── + +/** + * Must run on the WHOLE raw line BEFORE any backtick-splitting. + * [`label`](target) straddles a backtick boundary β€” splitting first breaks it. + */ +function stripRustdocLinksFromLine(line: string): string { + // Reference-style link definition line β€” drop entirely + if (/^\s*\[`?[^\]]+`?\]:\s*\S/.test(line)) return ''; + // Inline backtick-label link β†’ keep just the backtick span + line = line.replace(/\[(`[^`]+`)\]\([^)]*\)/g, '$1'); + // Inline plain-text-label link (non-http) β†’ keep just the label + line = line.replace(/\[([^\]]+)\]\((?!https?:\/\/)([^)]*)\)/g, '$1'); + return line; +} + +// ── Doc comment processing ──────────────────────────────────────────────────── + +/** + * Converts a raw FRAME metadata docs array to Markdown for the summary field. + * + * The returned string is intended to follow "- **summary**: " in a list item. + * The first line has no indent (sits inline with the label). + * All subsequent lines are indented 4 spaces (CommonMark list continuation). + * + * Token types recognised: + * + * section β€” any line starting with # (one or more) + * β€” OR a bare "Word:" / "Two Words:" label on its own line + * β†’ rendered as **Label:** (bold, indented, with colon) + * + * item β€” * `foo` - desc or - `foo` - desc (FRAME style) + * β€” * some plain text (any * or - lead) + * β€” * 'param' (Type): + tab-desc line (SubtensorModule style) + * β†’ rendered as: - text (indented) + * + * numbered β€” 1. foo + * β†’ rendered as: 1. foo (indented) + * + * prose β€” everything else; consecutive lines joined with a space + * β€” first-line prose: no indent + * β€” subsequent prose blocks (after blank/section/item): indented + * β€” leading ---, --, β€” stripped when at the very start + * β€” O(N), O(1) etc. wrapped in backticks + */ +export function extractDocs(docs: any[]): string { + if (!docs || docs.length === 0) return ''; + + // ── Regexes ────────────────────────────────────────────────────────────── + + // Any line starting with one or more # β†’ section header + const HASH_SECTION_RE = /^\s*#+\s*(.+?):?\s*$/; + + // Bare "Label:" or "Two Words:" on its own line (no # prefix) β†’ section + // Must be only capitalised-word(s) followed by a colon, nothing else. + // Bare section: a single word or two short words followed by colon, alone on line. + // Intentionally tight (max ~20 chars, no punctuation mid-word) to avoid matching + // prose sentences that end with a colon. + const BARE_SECTION_RE = /^\s*([A-Z][A-Za-z]{1,18}(?:\s+[A-Za-z]{1,12})?):\s*$/; + + // Standard FRAME list item: * `foo` - desc or - `foo` - desc + const FRAME_ITEM_RE = /^\s*[*-]\s+(`[^`]+`|\[[^\]]+\])\s*([-–—]\s*(.*))?$/; + + // Generic bullet: any line starting with * or - followed by content + // (catches plain prose bullets not covered by FRAME_ITEM_RE) + const GENERIC_ITEM_RE = /^\s*[*-]\s+(.+)$/; + + // SubtensorModule item: * 'param' (Type): or * 'param': + const SUBTENSOR_ITEM_RE = /^\s*\*\s+'([^']+)'(?:\s*\(([^)]*)\))?[:\s]*(.*)$/; + + // SubtensorModule tab-indented description: \t- description + const SUBTENSOR_DESC_RE = /^[\t ]+[-–]\s+(.+)$/; + + // Numbered list + const NUMBERED_RE = /^\s*(\d+)\.\s+(.+)$/; + + // ── Pass 1: tokenise ────────────────────────────────────────────────────── + + type Token = + | { t: 'blank' } + | { t: 'section'; text: string } + | { t: 'item'; text: string } + | { t: 'item_start'; text: string } + | { t: 'desc'; text: string } + | { t: 'numbered'; n: number; text: string } + | { t: 'prose'; text: string }; + + const tokens: Token[] = []; + + for (const raw of docs) { + const line = stripRustdocLinksFromLine(raw.toString()); + const trimmed = line.trim(); + + if (trimmed === '') { + tokens.push({ t: 'blank' }); + continue; + } + + // # heading β€” always a section + if (/^\s*#+/.test(trimmed)) { + const m = HASH_SECTION_RE.exec(trimmed); + const heading = m ? m[1].trim() : trimmed.replace(/^#+\s*/, '').trim(); + tokens.push({ t: 'section', text: capitalise(heading) }); + continue; + } + + // Bare "Label:" section (no # prefix) + if (BARE_SECTION_RE.test(trimmed)) { + const m = BARE_SECTION_RE.exec(trimmed)!; + tokens.push({ t: 'section', text: capitalise(m[1].trim()) }); + continue; + } + + // SubtensorModule tab-desc (must test the raw line for leading whitespace) + if (SUBTENSOR_DESC_RE.test(line)) { + const m = SUBTENSOR_DESC_RE.exec(line)!; + tokens.push({ t: 'desc', text: m[1].trim() }); + continue; + } + + // SubtensorModule item: * 'param' (Type): ... + { + const m = SUBTENSOR_ITEM_RE.exec(trimmed); + if (m) { + const name = m[1].trim(); + const type = m[2]?.trim() ?? ''; + const extra = m[3]?.trim() ?? ''; + const label = type ? `\`${name}\` (${type})` : `\`${name}\``; + tokens.push({ t: 'item_start', text: extra ? `${label} β€” ${extra}` : label }); + continue; + } + } + + // Standard FRAME item: * `foo` - desc + { + const m = FRAME_ITEM_RE.exec(trimmed); + if (m) { + const label = m[1]; + const desc = m[3]?.trim() ?? ''; + tokens.push({ t: 'item', text: desc ? `${label} β€” ${desc}` : label }); + continue; + } + } + + // Generic bullet (any * or - lead not matched above) + { + const m = GENERIC_ITEM_RE.exec(trimmed); + if (m) { + tokens.push({ t: 'item', text: m[1].trim() }); + continue; + } + } + + // Numbered list + { + const m = NUMBERED_RE.exec(trimmed); + if (m) { + tokens.push({ t: 'numbered', n: parseInt(m[1], 10), text: m[2].trim() }); + continue; + } + } + + tokens.push({ t: 'prose', text: trimmed }); + } + + // ── Pass 2: merge SubtensorModule item_start + desc ─────────────────────── + + const merged: Exclude[] = []; + for (let i = 0; i < tokens.length; i++) { + const tok = tokens[i]; + if (tok.t === 'item_start') { + let j = i + 1; + while (j < tokens.length && tokens[j].t === 'blank') j++; + if (j < tokens.length && tokens[j].t === 'desc') { + const desc = (tokens[j] as { t: 'desc'; text: string }).text; + merged.push({ t: 'item', text: `${tok.text} β€” ${desc}` }); + i = j; + while (i + 1 < tokens.length && tokens[i + 1].t === 'blank') i++; + } else { + merged.push({ t: 'item', text: tok.text }); + } + continue; + } + if (tok.t === 'desc') { + // Orphaned desc β€” treat as prose + merged.push({ t: 'prose', text: tok.text }); + continue; + } + merged.push(tok as any); + } + + // ── Pass 3: render ──────────────────────────────────────────────────────── + // First prose line: no indent (sits inline with "- **summary**: "). + // Everything after: 4-space indent (CommonMark list continuation). + // + // Paragraph-break rule: + // A blank line in the source creates a real paragraph break in the output + // ONLY when it separates structurally different content (e.g. prose β†’ + // section, prose β†’ list, or two distinct prose paragraphs separated by a + // genuine blank). A line break with no blank line before it is NEVER a + // paragraph break β€” consecutive prose lines always join onto the same line. + // + // Implementation: we track `prevNonBlank` so that after seeing a blank we + // know whether what came before was prose. If prose preceded and prose + // follows the blank, we merge (the blank was just a soft wrap in the source). + // If a section/item/numbered follows the blank, we keep the blank as a real + // paragraph separator. + + const INDENT = ' '; + const out: string[] = []; + let prev: Token['t'] | null = null; + let prevNonBlank: Token['t'] | null = null; + let firstProseDone = false; + let pendingBlank = false; // a blank was seen; defer emitting it + + for (const tok of merged) { + + if (tok.t === 'blank') { + pendingBlank = true; + prev = 'blank'; + continue; + } + + if (tok.t === 'section') { + // A section header ALWAYS needs a blank line before it so Markdown renderers + // start it on a new paragraph. Without this, items immediately followed by a + // section header (no blank line in source) render the header inline. + if (out.length > 0 && out[out.length - 1] !== '') out.push(''); + pendingBlank = false; + out.push(`${INDENT}**${tok.text}:**`); + out.push(''); + prev = 'section'; + prevNonBlank = 'section'; + firstProseDone = true; + continue; + } + + if (tok.t === 'item') { + // Flush a pending blank before a list item (real separation) + if (pendingBlank && out.length > 0 && out[out.length - 1] !== '') out.push(''); + pendingBlank = false; + const itemText = tok.text.replace(/\bO\(([^)]+)\)/g, '`O($1)`'); + out.push(`${INDENT}- ${itemText}`); + prev = 'item'; + prevNonBlank = 'item'; + firstProseDone = true; + continue; + } + + if (tok.t === 'numbered') { + if (pendingBlank && out.length > 0 && out[out.length - 1] !== '') out.push(''); + pendingBlank = false; + out.push(`${INDENT}${tok.n}. ${tok.text}`); + prev = 'numbered'; + prevNonBlank = 'numbered'; + firstProseDone = true; + continue; + } + + // prose + let proseText = tok.text; + + // Strip leading dashes at the very start of the summary + if (out.length === 0 || prevNonBlank === 'section') { + proseText = proseText.replace(/^[-–—]{1,3}\s*/, '').trim(); + } + if (!proseText) { pendingBlank = false; prev = 'prose'; continue; } + + proseText = proseText.replace(/\bO\(([^)]+)\)/g, '`O($1)`'); + + // Capture before clearing β€” used to decide join vs break below + const hadBlank = pendingBlank; + pendingBlank = false; + + // Paragraph-break rule: + // hadBlank === true β†’ a blank line separated this prose from the previous + // β†’ REAL paragraph break β€” emit blank, start new line + // hadBlank === false β†’ consecutive lines, no blank between them + // β†’ source line-wrap β€” JOIN onto the current line + + if (hadBlank && out.length > 0 && out[out.length - 1] !== '') { + // Real paragraph break + out.push(''); + } + + const lastLine = out.length > 0 ? out[out.length - 1] : ''; + const canJoin = !hadBlank && + prevNonBlank === 'prose' && + lastLine !== '' && + !lastLine.startsWith(INDENT + '-') && + !lastLine.startsWith(INDENT + '**'); + + if (canJoin) { + out[out.length - 1] += ' ' + proseText; + } else { + const indent = firstProseDone ? INDENT : ''; + out.push(`${indent}${proseText}`); + firstProseDone = true; + } + prev = 'prose'; + prevNonBlank = 'prose'; + } + + while (out.length > 0 && out[out.length - 1] === '') out.pop(); + if (out.length === 0) return ''; + + return escapeMdx(out.join('\n')); +} + +function capitalise(s: string): string { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +/** + * Escapes MDX-breaking characters in prose outside backtick spans. + */ +export function escapeMdx(text: string): string { + const parts = text.split(/(`[^`]*`)/g); + return parts + .map((part, i) => { + if (i % 2 === 0) { + return part.replace(/(ns: Record): [string, T][] { + const entries = Object.entries(ns); + entries.sort(([a], [b]) => { + if (a === 'SubtensorModule') return -1; + if (b === 'SubtensorModule') return 1; + return a.localeCompare(b); + }); + return entries; +} diff --git a/src/subtensor-docs-gen/tsconfig.json b/src/subtensor-docs-gen/tsconfig.json new file mode 100644 index 00000000..b4ec8c1b --- /dev/null +++ b/src/subtensor-docs-gen/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/src/subtensor-docs-gen/yarn.lock b/src/subtensor-docs-gen/yarn.lock new file mode 100644 index 00000000..23b30447 --- /dev/null +++ b/src/subtensor-docs-gen/yarn.lock @@ -0,0 +1,687 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@noble/curves@^1.3.0": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.7.tgz#79d04b4758a43e4bca2cbdc62e7771352fa6b951" + integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== + dependencies: + "@noble/hashes" "1.8.0" + +"@noble/hashes@1.8.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + +"@polkadot-api/client@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/client/-/client-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#5d6b863f63f5c6ecd4183fcf0c5c84dd349f7627" + integrity sha512-0fqK6pUKcGHSG2pBvY+gfSS+1mMdjd/qRygAcKI5d05tKsnZLRnmhb9laDguKmGEIB0Bz9vQqNK3gIN/cfvVwg== + dependencies: + "@polkadot-api/metadata-builders" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/substrate-bindings" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/substrate-client" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/utils" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + +"@polkadot-api/json-rpc-provider-proxy@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#cc28fb801db6a47824261a709ab924ec6951eb96" + integrity sha512-0hZ8vtjcsyCX8AyqP2sqUHa1TFFfxGWmlXJkit0Nqp9b32MwZqn5eaUAiV2rNuEpoglKOdKnkGtUF8t5MoodKw== + +"@polkadot-api/json-rpc-provider@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#2f71bfb192d28dd4c400ef8b1c5f934c676950f3" + integrity sha512-EaUS9Fc3wsiUr6ZS43PQqaRScW7kM6DYbuM/ou0aYjm8N9MBqgDbGm2oL6RE1vAVmOfEuHcXZuZkhzWtyvQUtA== + +"@polkadot-api/metadata-builders@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/metadata-builders/-/metadata-builders-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#085db2a3c7b100626b2fae3be35a32a24ea7714f" + integrity sha512-BD7rruxChL1VXt0icC2gD45OtT9ofJlql0qIllHSRYgama1CR2Owt+ApInQxB+lWqM+xNOznZRpj8CXNDvKIMg== + dependencies: + "@polkadot-api/substrate-bindings" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/utils" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + +"@polkadot-api/substrate-bindings@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/substrate-bindings/-/substrate-bindings-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#f836a554a9ead6fb6356079c725cd53f87238932" + integrity sha512-N4vdrZopbsw8k57uG58ofO7nLXM4Ai7835XqakN27MkjXMp5H830A1KJE0L9sGQR7ukOCDEIHHcwXVrzmJ/PBg== + dependencies: + "@noble/hashes" "^1.3.1" + "@polkadot-api/utils" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@scure/base" "^1.1.1" + scale-ts "^1.6.0" + +"@polkadot-api/substrate-client@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/substrate-client/-/substrate-client-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#55ae463f4143495e328465dd16b03e71663ef4c4" + integrity sha512-lcdvd2ssUmB1CPzF8s2dnNOqbrDa+nxaaGbuts+Vo8yjgSKwds2Lo7Oq+imZN4VKW7t9+uaVcKFLMF7PdH0RWw== + +"@polkadot-api/utils@0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": + version "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + resolved "https://registry.yarnpkg.com/@polkadot-api/utils/-/utils-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz#759698dcf948745ea37cc5ab6abd49a00f1b0c31" + integrity sha512-0CYaCjfLQJTCRCiYvZ81OncHXEKPzAexCMoVloR+v2nl/O2JRya/361MtPkeNLC6XBoaEgLAG9pWQpH3WePzsw== + +"@polkadot/api-augment@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-10.13.1.tgz#dd3670a2f1a581c38b857ad3b0805b6581099c63" + integrity sha512-IAKaCp19QxgOG4HKk9RAgUgC/VNVqymZ2GXfMNOZWImZhxRIbrK+raH5vN2MbWwtVHpjxyXvGsd1RRhnohI33A== + dependencies: + "@polkadot/api-base" "10.13.1" + "@polkadot/rpc-augment" "10.13.1" + "@polkadot/types" "10.13.1" + "@polkadot/types-augment" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/util" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/api-base@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-10.13.1.tgz#efed5bb31e38244b6a68ce56138b97ad82101426" + integrity sha512-Okrw5hjtEjqSMOG08J6qqEwlUQujTVClvY1/eZkzKwNzPelWrtV6vqfyJklB7zVhenlxfxqhZKKcY7zWSW/q5Q== + dependencies: + "@polkadot/rpc-core" "10.13.1" + "@polkadot/types" "10.13.1" + "@polkadot/util" "^12.6.2" + rxjs "^7.8.1" + tslib "^2.6.2" + +"@polkadot/api-derive@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-10.13.1.tgz#d8827ee83124f3b3f664c415cdde9c6b909e5145" + integrity sha512-ef0H0GeCZ4q5Om+c61eLLLL29UxFC2/u/k8V1K2JOIU+2wD5LF7sjAoV09CBMKKHfkLenRckVk2ukm4rBqFRpg== + dependencies: + "@polkadot/api" "10.13.1" + "@polkadot/api-augment" "10.13.1" + "@polkadot/api-base" "10.13.1" + "@polkadot/rpc-core" "10.13.1" + "@polkadot/types" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/util" "^12.6.2" + "@polkadot/util-crypto" "^12.6.2" + rxjs "^7.8.1" + tslib "^2.6.2" + +"@polkadot/api@10.13.1", "@polkadot/api@^10.11.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-10.13.1.tgz#47586c070d3fe13a0acc93a8aa9c3a53791284fb" + integrity sha512-YrKWR4TQR5CDyGkF0mloEUo7OsUA+bdtENpJGOtNavzOQUDEbxFE0PVzokzZfVfHhHX2CojPVmtzmmLxztyJkg== + dependencies: + "@polkadot/api-augment" "10.13.1" + "@polkadot/api-base" "10.13.1" + "@polkadot/api-derive" "10.13.1" + "@polkadot/keyring" "^12.6.2" + "@polkadot/rpc-augment" "10.13.1" + "@polkadot/rpc-core" "10.13.1" + "@polkadot/rpc-provider" "10.13.1" + "@polkadot/types" "10.13.1" + "@polkadot/types-augment" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/types-create" "10.13.1" + "@polkadot/types-known" "10.13.1" + "@polkadot/util" "^12.6.2" + "@polkadot/util-crypto" "^12.6.2" + eventemitter3 "^5.0.1" + rxjs "^7.8.1" + tslib "^2.6.2" + +"@polkadot/keyring@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-12.6.2.tgz#6067e6294fee23728b008ac116e7e9db05cecb9b" + integrity sha512-O3Q7GVmRYm8q7HuB3S0+Yf/q/EB2egKRRU3fv9b3B7V+A52tKzA+vIwEmNVaD1g5FKW9oB97rmpggs0zaKFqHw== + dependencies: + "@polkadot/util" "12.6.2" + "@polkadot/util-crypto" "12.6.2" + tslib "^2.6.2" + +"@polkadot/networks@12.6.2", "@polkadot/networks@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-12.6.2.tgz#791779fee1d86cc5b6cd371858eea9b7c3f8720d" + integrity sha512-1oWtZm1IvPWqvMrldVH6NI2gBoCndl5GEwx7lAuQWGr7eNL+6Bdc5K3Z9T0MzFvDGoi2/CBqjX9dRKo39pDC/w== + dependencies: + "@polkadot/util" "12.6.2" + "@substrate/ss58-registry" "^1.44.0" + tslib "^2.6.2" + +"@polkadot/rpc-augment@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-10.13.1.tgz#83317b46c5ab86104cca2bdc336199db0c25b798" + integrity sha512-iLsWUW4Jcx3DOdVrSHtN0biwxlHuTs4QN2hjJV0gd0jo7W08SXhWabZIf9mDmvUJIbR7Vk+9amzvegjRyIf5+A== + dependencies: + "@polkadot/rpc-core" "10.13.1" + "@polkadot/types" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/util" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/rpc-core@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-10.13.1.tgz#a7ea9db8997b68aa6724f28ba76125a73e925575" + integrity sha512-eoejSHa+/tzHm0vwic62/aptTGbph8vaBpbvLIK7gd00+rT813ROz5ckB1CqQBFB23nHRLuzzX/toY8ID3xrKw== + dependencies: + "@polkadot/rpc-augment" "10.13.1" + "@polkadot/rpc-provider" "10.13.1" + "@polkadot/types" "10.13.1" + "@polkadot/util" "^12.6.2" + rxjs "^7.8.1" + tslib "^2.6.2" + +"@polkadot/rpc-provider@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-10.13.1.tgz#7e17f7be7d9a104b797d8f5aa8f1ed69f800f841" + integrity sha512-oJ7tatVXYJ0L7NpNiGd69D558HG5y5ZDmH2Bp9Dd4kFTQIiV8A39SlWwWUPCjSsen9lqSvvprNLnG/VHTpenbw== + dependencies: + "@polkadot/keyring" "^12.6.2" + "@polkadot/types" "10.13.1" + "@polkadot/types-support" "10.13.1" + "@polkadot/util" "^12.6.2" + "@polkadot/util-crypto" "^12.6.2" + "@polkadot/x-fetch" "^12.6.2" + "@polkadot/x-global" "^12.6.2" + "@polkadot/x-ws" "^12.6.2" + eventemitter3 "^5.0.1" + mock-socket "^9.3.1" + nock "^13.5.0" + tslib "^2.6.2" + optionalDependencies: + "@substrate/connect" "0.8.8" + +"@polkadot/types-augment@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-10.13.1.tgz#8f39a46a1a3e100be03cbae06f43a043cb25c337" + integrity sha512-TcrLhf95FNFin61qmVgOgayzQB/RqVsSg9thAso1Fh6pX4HSbvI35aGPBAn3SkA6R+9/TmtECirpSNLtIGFn0g== + dependencies: + "@polkadot/types" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/util" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/types-codec@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-10.13.1.tgz#f70cd617160b467685ef3ce5195a04142255ba7b" + integrity sha512-AiQ2Vv2lbZVxEdRCN8XSERiWlOWa2cTDLnpAId78EnCtx4HLKYQSd+Jk9Y4BgO35R79mchK4iG+w6gZ+ukG2bg== + dependencies: + "@polkadot/util" "^12.6.2" + "@polkadot/x-bigint" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/types-create@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-10.13.1.tgz#99470816d0d2ca32a6a5ce6d701b4199e8700f66" + integrity sha512-Usn1jqrz35SXgCDAqSXy7mnD6j4RvB4wyzTAZipFA6DGmhwyxxIgOzlWQWDb+1PtPKo9vtMzen5IJ+7w5chIeA== + dependencies: + "@polkadot/types-codec" "10.13.1" + "@polkadot/util" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/types-known@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-10.13.1.tgz#8cca2d3f2c4ef67849f66ba4a35856063ec61f5f" + integrity sha512-uHjDW05EavOT5JeU8RbiFWTgPilZ+odsCcuEYIJGmK+es3lk/Qsdns9Zb7U7NJl7eJ6OWmRtyrWsLs+bU+jjIQ== + dependencies: + "@polkadot/networks" "^12.6.2" + "@polkadot/types" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/types-create" "10.13.1" + "@polkadot/util" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/types-support@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-10.13.1.tgz#d4b58c8d9bcbb8e897a255d9a66c217dcaa6ead4" + integrity sha512-4gEPfz36XRQIY7inKq0HXNVVhR6HvXtm7yrEmuBuhM86LE0lQQBkISUSgR358bdn2OFSLMxMoRNoh3kcDvdGDQ== + dependencies: + "@polkadot/util" "^12.6.2" + tslib "^2.6.2" + +"@polkadot/types@10.13.1", "@polkadot/types@^10.11.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-10.13.1.tgz#979d652dc11af9cb8b32e7a55839e9762532755d" + integrity sha512-Hfvg1ZgJlYyzGSAVrDIpp3vullgxrjOlh/CSThd/PI4TTN1qHoPSFm2hs77k3mKkOzg+LrWsLE0P/LP2XddYcw== + dependencies: + "@polkadot/keyring" "^12.6.2" + "@polkadot/types-augment" "10.13.1" + "@polkadot/types-codec" "10.13.1" + "@polkadot/types-create" "10.13.1" + "@polkadot/util" "^12.6.2" + "@polkadot/util-crypto" "^12.6.2" + rxjs "^7.8.1" + tslib "^2.6.2" + +"@polkadot/util-crypto@12.6.2", "@polkadot/util-crypto@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-12.6.2.tgz#d2d51010e8e8ca88951b7d864add797dad18bbfc" + integrity sha512-FEWI/dJ7wDMNN1WOzZAjQoIcCP/3vz3wvAp5QQm+lOrzOLj0iDmaIGIcBkz8HVm3ErfSe/uKP0KS4jgV/ib+Mg== + dependencies: + "@noble/curves" "^1.3.0" + "@noble/hashes" "^1.3.3" + "@polkadot/networks" "12.6.2" + "@polkadot/util" "12.6.2" + "@polkadot/wasm-crypto" "^7.3.2" + "@polkadot/wasm-util" "^7.3.2" + "@polkadot/x-bigint" "12.6.2" + "@polkadot/x-randomvalues" "12.6.2" + "@scure/base" "^1.1.5" + tslib "^2.6.2" + +"@polkadot/util@12.6.2", "@polkadot/util@^12.6.1", "@polkadot/util@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-12.6.2.tgz#9396eff491221e1f0fd28feac55fc16ecd61a8dc" + integrity sha512-l8TubR7CLEY47240uki0TQzFvtnxFIO7uI/0GoWzpYD/O62EIAMRsuY01N4DuwgKq2ZWD59WhzsLYmA5K6ksdw== + dependencies: + "@polkadot/x-bigint" "12.6.2" + "@polkadot/x-global" "12.6.2" + "@polkadot/x-textdecoder" "12.6.2" + "@polkadot/x-textencoder" "12.6.2" + "@types/bn.js" "^5.1.5" + bn.js "^5.2.1" + tslib "^2.6.2" + +"@polkadot/wasm-bridge@7.5.4": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-7.5.4.tgz#8b938b3def8f6b0bccbe5555c076efafe484fbc5" + integrity sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA== + dependencies: + "@polkadot/wasm-util" "7.5.4" + tslib "^2.7.0" + +"@polkadot/wasm-crypto-asmjs@7.5.4": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.4.tgz#d5ef668e0fa194cec5d54c11388359447b466ad0" + integrity sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA== + dependencies: + tslib "^2.7.0" + +"@polkadot/wasm-crypto-init@7.5.4": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.4.tgz#3ebb59a76a7a2ec05bde3fd619197fb1a164aff9" + integrity sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg== + dependencies: + "@polkadot/wasm-bridge" "7.5.4" + "@polkadot/wasm-crypto-asmjs" "7.5.4" + "@polkadot/wasm-crypto-wasm" "7.5.4" + "@polkadot/wasm-util" "7.5.4" + tslib "^2.7.0" + +"@polkadot/wasm-crypto-wasm@7.5.4": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.4.tgz#bc4fb9aa707cd8218c8143d330ccf2db989a2726" + integrity sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw== + dependencies: + "@polkadot/wasm-util" "7.5.4" + tslib "^2.7.0" + +"@polkadot/wasm-crypto@^7.3.2": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-7.5.4.tgz#c83b91623e96e168262935a7f2e7c1ffd1875249" + integrity sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g== + dependencies: + "@polkadot/wasm-bridge" "7.5.4" + "@polkadot/wasm-crypto-asmjs" "7.5.4" + "@polkadot/wasm-crypto-init" "7.5.4" + "@polkadot/wasm-crypto-wasm" "7.5.4" + "@polkadot/wasm-util" "7.5.4" + tslib "^2.7.0" + +"@polkadot/wasm-util@7.5.4", "@polkadot/wasm-util@^7.3.2": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-7.5.4.tgz#dcaad33f44dc18ff22762c4f1572a5c9bfa77579" + integrity sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w== + dependencies: + tslib "^2.7.0" + +"@polkadot/x-bigint@12.6.2", "@polkadot/x-bigint@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-12.6.2.tgz#59b7a615f205ae65e1ac67194aefde94d3344580" + integrity sha512-HSIk60uFPX4GOFZSnIF7VYJz7WZA7tpFJsne7SzxOooRwMTWEtw3fUpFy5cYYOeLh17/kHH1Y7SVcuxzVLc74Q== + dependencies: + "@polkadot/x-global" "12.6.2" + tslib "^2.6.2" + +"@polkadot/x-fetch@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-12.6.2.tgz#b1bca028db90263bafbad2636c18d838d842d439" + integrity sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw== + dependencies: + "@polkadot/x-global" "12.6.2" + node-fetch "^3.3.2" + tslib "^2.6.2" + +"@polkadot/x-global@12.6.2", "@polkadot/x-global@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-12.6.2.tgz#31d4de1c3d4c44e4be3219555a6d91091decc4ec" + integrity sha512-a8d6m+PW98jmsYDtAWp88qS4dl8DyqUBsd0S+WgyfSMtpEXu6v9nXDgPZgwF5xdDvXhm+P0ZfVkVTnIGrScb5g== + dependencies: + tslib "^2.6.2" + +"@polkadot/x-randomvalues@12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-12.6.2.tgz#13fe3619368b8bf5cb73781554859b5ff9d900a2" + integrity sha512-Vr8uG7rH2IcNJwtyf5ebdODMcr0XjoCpUbI91Zv6AlKVYOGKZlKLYJHIwpTaKKB+7KPWyQrk4Mlym/rS7v9feg== + dependencies: + "@polkadot/x-global" "12.6.2" + tslib "^2.6.2" + +"@polkadot/x-textdecoder@12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-12.6.2.tgz#b86da0f8e8178f1ca31a7158257e92aea90b10e4" + integrity sha512-M1Bir7tYvNappfpFWXOJcnxUhBUFWkUFIdJSyH0zs5LmFtFdbKAeiDXxSp2Swp5ddOZdZgPac294/o2TnQKN1w== + dependencies: + "@polkadot/x-global" "12.6.2" + tslib "^2.6.2" + +"@polkadot/x-textencoder@12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-12.6.2.tgz#81d23bd904a2c36137a395c865c5fefa21abfb44" + integrity sha512-4N+3UVCpI489tUJ6cv3uf0PjOHvgGp9Dl+SZRLgFGt9mvxnvpW/7+XBADRMtlG4xi5gaRK7bgl5bmY6OMDsNdw== + dependencies: + "@polkadot/x-global" "12.6.2" + tslib "^2.6.2" + +"@polkadot/x-ws@^12.6.2": + version "12.6.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-12.6.2.tgz#b99094d8e53a03be1de903d13ba59adaaabc767a" + integrity sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw== + dependencies: + "@polkadot/x-global" "12.6.2" + tslib "^2.6.2" + ws "^8.15.1" + +"@scure/base@^1.1.1", "@scure/base@^1.1.5": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" + integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== + +"@substrate/connect-extension-protocol@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz#2cf8f2eaf1879308d307a1a08df83cd5db918fe0" + integrity sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA== + +"@substrate/connect-known-chains@^1.1.1": + version "1.10.3" + resolved "https://registry.yarnpkg.com/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz#71a89864f13626c412fa0a9d0ffc4f6ca39fdcec" + integrity sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w== + +"@substrate/connect@0.8.8": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.8.8.tgz#80879f2241e2bd4f24a9aa25d7997fd91a5e68e3" + integrity sha512-zwaxuNEVI9bGt0rT8PEJiXOyebLIo6QN1SyiAHRPBOl6g3Sy0KKdSN8Jmyn++oXhVRD8aIe75/V8ZkS81T+BPQ== + dependencies: + "@substrate/connect-extension-protocol" "^2.0.0" + "@substrate/connect-known-chains" "^1.1.1" + "@substrate/light-client-extension-helpers" "^0.0.4" + smoldot "2.0.22" + +"@substrate/light-client-extension-helpers@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-0.0.4.tgz#a5958d5c1aac7df69f55bd90991aa935500f8124" + integrity sha512-vfKcigzL0SpiK+u9sX6dq2lQSDtuFLOxIJx2CKPouPEHIs8C+fpsufn52r19GQn+qDhU8POMPHOVoqLktj8UEA== + dependencies: + "@polkadot-api/client" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/json-rpc-provider" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/json-rpc-provider-proxy" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@polkadot-api/substrate-client" "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + "@substrate/connect-extension-protocol" "^2.0.0" + "@substrate/connect-known-chains" "^1.1.1" + rxjs "^7.8.1" + +"@substrate/ss58-registry@^1.44.0": + version "1.51.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz#39e0341eb4069c2d3e684b93f0d8cb0bec572383" + integrity sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ== + +"@tsconfig/node10@^1.0.7": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" + integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/bn.js@^5.1.5": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.2.0.tgz#4349b9710e98f9ab3cdc50f1c5e4dcbd8ef29c80" + integrity sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "25.6.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.6.0.tgz#4e09bad9b469871f2d0f68140198cbd714f4edca" + integrity sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ== + dependencies: + undici-types "~7.19.0" + +"@types/node@^20.0.0": + version "20.19.39" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.39.tgz#e98a3b575574070cd34b784bd173767269f95e99" + integrity sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw== + dependencies: + undici-types "~6.21.0" + +acorn-walk@^8.1.1: + version "8.3.5" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.5.tgz#8a6b8ca8fc5b34685af15dabb44118663c296496" + integrity sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +bn.js@^5.2.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.3.tgz#16a9e409616b23fef3ccbedb8d42f13bff80295e" + integrity sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +debug@^4.1.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +diff@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" + integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== + +eventemitter3@^5.0.1: + version "5.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.4.tgz#a86d66170433712dde814707ac52b5271ceb1feb" + integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +mock-socket@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.3.1.tgz#24fb00c2f573c84812aa4a24181bb025de80cc8e" + integrity sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nock@^13.5.0: + version "13.5.6" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.6.tgz#5e693ec2300bbf603b61dae6df0225673e6c4997" + integrity sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + propagate "^2.0.0" + +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + +rxjs@^7.8.1: + version "7.8.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== + dependencies: + tslib "^2.1.0" + +scale-ts@^1.6.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/scale-ts/-/scale-ts-1.6.1.tgz#45151e156d6c04792223c39d8e7484ce926445f2" + integrity sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g== + +smoldot@2.0.22: + version "2.0.22" + resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-2.0.22.tgz#1e924d2011a31c57416e79a2b97a460f462a31c7" + integrity sha512-B50vRgTY6v3baYH6uCgL15tfaag5tcS2o/P5q1OiXcKGv1axZDfz2dzzMuIkVpyMR2ug11F6EAtQlmYBQd292g== + dependencies: + ws "^8.8.1" + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@^2.1.0, tslib@^2.6.2, tslib@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +typescript@^5.3.0: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +undici-types@~7.19.0: + version "7.19.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.19.2.tgz#1b67fc26d0f157a0cba3a58a5b5c1e2276b8ba2a" + integrity sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + +ws@^8.15.1, ws@^8.8.1: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" + integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==