Merge pull request #105 from polkadot-api/vo/rpcs

feat: monaco editor + json schema for rpc methods
This commit is contained in:
Victor Oliva
2025-11-15 10:03:57 -03:00
committed by GitHub
9 changed files with 211 additions and 17 deletions
+1 -1
View File
@@ -16,6 +16,7 @@
"dependencies": {
"@acala-network/chopsticks-core": "^1.2.4",
"@ledgerhq/hw-transport-webusb": "^6.29.13",
"@monaco-editor/react": "^4.7.0",
"@polkadot-api/descriptors": "file:.papi/descriptors",
"@polkadot-api/json-rpc-provider-proxy": "^0.2.6",
"@polkadot-api/metadata-builders": "^0.13.7",
@@ -40,7 +41,6 @@
"@radix-ui/react-tooltip": "^1.2.8",
"@react-rxjs/core": "^0.10.8",
"@react-rxjs/utils": "^0.9.7",
"buffer": "^6.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"idb-keyval": "^6.2.2",
+49 -3
View File
@@ -19,6 +19,9 @@ importers:
'@ledgerhq/hw-transport-webusb':
specifier: ^6.29.13
version: 6.29.13
'@monaco-editor/react':
specifier: ^4.7.0
version: 4.7.0(monaco-editor@0.54.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@polkadot-api/descriptors':
specifier: file:.papi/descriptors
version: file:.papi/descriptors(polkadot-api@1.20.4(jiti@2.6.1)(postcss@8.5.6)(rxjs@7.8.2)(yaml@2.6.1))
@@ -91,9 +94,6 @@ importers:
'@react-rxjs/utils':
specifier: ^0.9.7
version: 0.9.7(@react-rxjs/core@0.10.8(react@19.2.0)(rxjs@7.8.2))(react@19.2.0)(rxjs@7.8.2)
buffer:
specifier: ^6.0.3
version: 6.0.3
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -589,6 +589,16 @@ packages:
'@polkadot-api/substrate-bindings': '*'
'@polkadot-api/utils': '*'
'@monaco-editor/loader@1.6.1':
resolution: {integrity: sha512-w3tEnj9HYEC73wtjdpR089AqkUPskFRcdkxsiSFt3SoUc3OHpmu+leP94CXBm4mHfefmhsdfI0ZQu6qJ0wgtPg==}
'@monaco-editor/react@4.7.0':
resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==}
peerDependencies:
monaco-editor: '>= 0.25.0 < 1'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@msgpack/msgpack@3.1.2':
resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==}
engines: {node: '>= 18'}
@@ -2240,6 +2250,9 @@ packages:
dijkstrajs@1.0.3:
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
dompurify@3.1.7:
resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -2750,6 +2763,11 @@ packages:
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
marked@14.0.0:
resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
engines: {node: '>= 18'}
hasBin: true
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -2783,6 +2801,9 @@ packages:
resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
engines: {node: '>= 8'}
monaco-editor@0.54.0:
resolution: {integrity: sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -3281,6 +3302,9 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
state-local@1.0.7:
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
stdin-discarder@0.2.2:
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
engines: {node: '>=18'}
@@ -4122,6 +4146,17 @@ snapshots:
'@polkadot-api/substrate-bindings': 0.16.5
'@polkadot-api/utils': 0.2.0
'@monaco-editor/loader@1.6.1':
dependencies:
state-local: 1.0.7
'@monaco-editor/react@4.7.0(monaco-editor@0.54.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@monaco-editor/loader': 1.6.1
monaco-editor: 0.54.0
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
'@msgpack/msgpack@3.1.2': {}
'@noble/ciphers@1.3.0': {}
@@ -6656,6 +6691,8 @@ snapshots:
dijkstrajs@1.0.3: {}
dompurify@3.1.7: {}
eastasianwidth@0.2.0: {}
electron-to-chromium@1.5.250: {}
@@ -7154,6 +7191,8 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
marked@14.0.0: {}
merge2@1.4.1: {}
micromatch@4.0.8:
@@ -7184,6 +7223,11 @@ snapshots:
mock-socket@9.3.1: {}
monaco-editor@0.54.0:
dependencies:
dompurify: 3.1.7
marked: 14.0.0
ms@2.1.3: {}
multiformats@9.9.0: {}
@@ -7792,6 +7836,8 @@ snapshots:
split2@4.2.0: {}
state-local@1.0.7: {}
stdin-discarder@0.2.2: {}
string-width@4.2.3:
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "chain_getBlock params",
"oneOf": [
{
"type": "object",
"description": "{} or { hash }.",
"maxProperties": 1,
"additionalProperties": false,
"properties": { "hash": { "$ref": "./common.json#/$defs/hex" } }
},
{
"type": "array",
"description": "[] or [hash].",
"maxItems": 1,
"items": { "$ref": "./common.json#/$defs/hex" }
}
]
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "chain_getBlockHash params",
"oneOf": [
{
"type": "array",
"description": "[] or [blockNumber].",
"maxItems": 1,
"items": {
"type": "number"
}
}
]
}
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "chain_getHeader params",
"oneOf": [
{
"type": "object",
"description": "{} or { hash }.",
"maxProperties": 1,
"additionalProperties": false,
"properties": { "hash": { "$ref": "./common.json#/$defs/hex" } }
},
{
"type": "array",
"description": "[] or [hash].",
"maxItems": 1,
"items": { "$ref": "./common.json#/$defs/hex" }
}
]
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "common.json",
"$defs": {
"hex": {
"type": "string",
"pattern": "^0x[0-9a-fA-F]*$",
"description": "0x-prefixed hex."
}
}
}
@@ -0,0 +1,35 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "state_getReadProof params",
"oneOf": [
{
"type": "array",
"description": "[keys] or [keys, at].",
"minItems": 1,
"maxItems": 2,
"prefixItems": [
{
"type": "array",
"description": "Storage keys to prove.",
"items": { "$ref": "./common.json#/$defs/hex" }
},
{ "$ref": "./common.json#/$defs/hex" }
]
},
{
"type": "object",
"description": "{ keys } or { keys, at } or { keys, hash }.",
"required": ["keys"],
"additionalProperties": false,
"properties": {
"keys": {
"type": "array",
"description": "Storage keys to prove.",
"items": { "$ref": "./common.json#/$defs/hex" }
},
"at": { "$ref": "./common.json#/$defs/hex" },
"hash": { "$ref": "./common.json#/$defs/hex" }
}
}
]
}
-2
View File
@@ -1,5 +1,3 @@
import { Buffer } from "buffer"
globalThis.Buffer = Buffer
import { dynamicBuilder$ } from "@/state/chains/chain.state"
import { RemoveSubscribe, Subscribe } from "@react-rxjs/core"
import { PolkaHubProvider } from "polkahub"
+63 -11
View File
@@ -1,14 +1,16 @@
import { ActionButton } from "@/components/ActionButton"
import { LoadingMetadata } from "@/components/Loading"
import { SearchableSelect } from "@/components/Select"
import { Textarea } from "@/components/ui/textarea"
import { withSubscribe } from "@/components/withSuspense"
import { chainClient$ } from "@/state/chains/chain.state"
import { state, useStateObservable } from "@react-rxjs/core"
import { useState } from "react"
import { lazy, Suspense, useRef, useState } from "react"
import { firstValueFrom, map, switchMap } from "rxjs"
import { RpcCallResults } from "./RpcCallResults"
import { addRpcCallQuery } from "./rpcCalls.state"
import { useTheme } from "@/ThemeProvider"
const Editor = lazy(() => import("@monaco-editor/react"))
const chainRpcMethods$ = state(
chainClient$.pipe(
@@ -19,11 +21,19 @@ const chainRpcMethods$ = state(
),
)
const SCHEMA_URL = window.location.origin
export const RpcCalls = withSubscribe(
() => {
const methods = useStateObservable(chainRpcMethods$)
const [method, setMethod] = useState<string | null>(null)
const [params, setParams] = useState<string>("[\n\n]")
const setSchema = useRef<((method: string | null) => void) | null>(null)
const [method, _setMethod] = useState<string | null>(null)
const [params, setParams] = useState<string>("[\n \n]")
const theme = useTheme()
const setMethod = (method: string | null) => {
_setMethod(method)
setSchema.current?.(method)
}
const submit = async () => {
const { client } = await firstValueFrom(chainClient$)
@@ -54,14 +64,56 @@ export const RpcCalls = withSubscribe(
allowCustomValue
/>
</label>
<label className="w-full">
<div className="w-full">
JSON Payload
<Textarea
className="w-full font-mono text-sm"
value={params}
onChange={(e) => setParams(e.target.value)}
/>
</label>
<Suspense
fallback={<div className="border rounded p-1 h-80 w-full" />}
>
<Editor
className="border rounded p-1 h-80"
language="json"
onMount={(editor, monaco) => {
const model = editor.getModel()
if (!model) return
setSchema.current = (method) => {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: method
? [
{
uri: `${SCHEMA_URL}/rpc_schemas/${method}.json`,
fileMatch: [model.uri.toString()],
},
]
: [],
enableSchemaRequest: true,
schemaRequest: "ignore",
})
monaco.languages.json.jsonDefaults.setModeConfiguration(
method
? { completionItems: true, diagnostics: true }
: {
completionItems: false,
diagnostics: true,
},
)
}
setSchema.current(method)
}}
value={params}
loading={<div className="border rounded p-1 h-80 w-full" />}
onChange={(v) => setParams(v ?? "[]")}
options={{
minimap: {
enabled: false,
},
lineNumbers: "off",
}}
theme={theme === "dark" ? "vs-dark" : "light"}
/>
</Suspense>
</div>
<ActionButton disabled={!isReady} onClick={submit}>
Call
</ActionButton>