Skip to content

feat(rpc): qn rpc — JSON-RPC calls with managed or custom endpoints#52

Merged
johnpmitsch merged 5 commits into
mainfrom
rpc_support
Jul 10, 2026
Merged

feat(rpc): qn rpc — JSON-RPC calls with managed or custom endpoints#52
johnpmitsch merged 5 commits into
mainfrom
rpc_support

Conversation

@johnpmitsch

@johnpmitsch johnpmitsch commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What this adds

qn rpc — make JSON-RPC calls straight from the terminal, with no endpoint to provision. The CLI mints and refreshes a short-lived session token for you against the account's Tooling Access endpoint, so a bare qn rpc call eth_blockNumber just works after a one-time enable. You can also point calls at your own fully-formed RPC URL (per-call or via config) when you don't want the managed endpoint.

The surface is two subcommands:

  • qn rpc call <method> [params] — the JSON-RPC call
  • qn rpc list-networks (alias ls) — discover the multichain endpoint's network keys

The end-user experience

First run

$ qn rpc call eth_blockNumber
Tooling Access is not enabled. Enable it now? [y/N] y
✓ Enabled Tooling Access
"0x1335f9a"

In scripts/agents, skip the prompt with --yes; without it in a non-TTY you get an actionable error (exit 5) and nothing is changed:

$ qn rpc call eth_blockNumber --yes        # auto-enables if needed
"0x1335f9a"

Making calls

# No params
qn rpc call eth_blockNumber

# Positional params (JSON array)
qn rpc call eth_getBalance '["0xabc...", "latest"]'

# Named params (JSON object)
qn rpc call eth_call '{"to":"0x...","data":"0x..."}'

# Params from a file (mirrors `qn sql query --file`)
qn rpc call eth_call --params-file params.json
qn rpc call eth_call -f params.json

# Params from stdin
echo '["0xabc...", "latest"]' | qn rpc call eth_getBalance -
cat params.json | qn rpc call eth_call -f -

Inline params, --params-file, and stdin are mutually exclusive ways to supply the same thing; the positional argument and --params-file can't be combined.

Results are schemaless JSON, so -o json (the default when piped) / yaml / toon control the shape; table/md fall back to JSON.

Multichain

The endpoint serves many chains. List the keys, then target one:

$ qn rpc list-networks
btc
ethereum-mainnet
polygon
solana-mainnet

$ qn rpc call getSlot --network solana-mainnet
"301234567"

Network keys are the endpoint's own multichain_urls keys and can differ from chain slugs (e.g. polygon, not matic). An unknown key points you at the discovery command:

$ qn rpc call getSlot --network matic
Error: unknown network key for this endpoint. Available: btc, ethereum-mainnet, polygon, solana-mainnet
Run 'qn rpc list-networks' to see valid keys.

The network map is cached in ~/.config/qn/networks.toml (per endpoint, 24h TTL), so --network calls reuse it.

Custom endpoint URL

Send a call to a fully-formed HTTP URL instead of the managed endpoint. The URL is treated as self-authenticating: no session token is minted or attached.

# Per call
qn rpc call eth_blockNumber --endpoint-url https://my-endpoint.example/rpc

Set a client-wide default in ~/.config/qn/config.toml so every call uses it without the flag:

[rpc]
endpoint_url = "https://my-endpoint.example/rpc"

A per-call --endpoint-url still overrides the config default. --endpoint-url and --network are mutually exclusive (a custom URL is not multichain-routed):

$ qn rpc call eth_blockNumber --endpoint-url https://x/rpc --network solana-mainnet
error: the argument '--endpoint-url <URL>' cannot be used with '--network <NETWORK>'

Managing Tooling Access directly

qn tooling-access status
qn tooling-access enable       # idempotent; requires an admin role
qn tooling-access disable

How it works

  • Managed lane: RPC calls authenticate with an ephemeral Bearer JWT minted via the Tooling Access token endpoint. The session token is cached under ~/.config/qn/tokens.toml (0600, scoped to the API key by a SHA-256 fingerprint), so subsequent calls skip the mint round trip while it's valid. Switching keys transparently invalidates a stale token.
  • Custom-URL lane: a separate path. No token is minted or attached, the token cache is never touched, and the Tooling Access enable/probe recovery is bypassed entirely — errors from a custom endpoint surface directly.
  • Retries: RPC calls are reads, so they honor --retries (transient 429/5xx/timeout backoff) on both lanes. Mutations (enable) never retry.
  • Auto-enable recovery: on the managed lane, a "not enabled" response or a connect failure against a stale token routes into the same enable flow (prompt on a TTY, --yes to auto-enable, actionable error otherwise), then retries.

SDK

Consumes the published quicknode-sdk 0.7 (adds the custom endpoint_url support to rpc.call and RpcConfig).

Testing

  • tests/rpc.rs: 21 tests — managed-lane happy paths (seeded token, mint-then-call, cache write-back), auto-enable flows (with/without --yes, connect-failure recovery), multichain routing + list, the custom-URL set (routes without minting, config default, flag-overrides-config, failure bypasses enable recovery, URL validation, --endpoint-url/--network conflict, unknown-network hint), and params-file input (file happy path, missing-file error, positional/--params-file conflict).
  • tests/tooling_access.rs: status/enable/disable.
  • Config [rpc] round-trip and validate_endpoint_url unit tests.

cargo test, cargo clippy --all-targets -- -D warnings, and cargo fmt --check all green.

Restructure `qn rpc` into explicit subcommands and add a way to target a
custom RPC endpoint instead of the account's Tooling Access endpoint.

- `qn rpc call <method> [params]` replaces the bare `qn rpc <method>` form,
  leaving room for future subcommands. Bare `qn rpc` prints help.
- `qn rpc list-networks` (alias `ls`) replaces the `--list-networks` flag.
- `qn rpc call --endpoint-url <URL>` sends a call to a fully-formed HTTP URL.
  The URL is self-authenticating: no session token is minted or attached, and
  the Tooling Access enable/probe recovery is bypassed entirely.
- `[rpc] endpoint_url` in config sets a client-wide default; a per-call
  `--endpoint-url` overrides it.
- `--endpoint-url` and `--network` are mutually exclusive (a custom URL is not
  multichain-routed), enforced at parse time.

An unknown `--network` key now surfaces a CLI error pointing at
`qn rpc list-networks` instead of the SDK's internal wording.

Consumes the published quicknode-sdk 0.7 (4-arg `rpc.call` +
`RpcConfig.endpoint_url`). Docs (README, agent context) and tests updated;
rpc.rs test count 18.
Add `--params-file <PATH>` / `-f` to `qn rpc call`, mirroring `qn sql query
--file`: read the JSON-RPC params from a file, or from stdin when the path is
`-`. Mutually exclusive with the positional params argument (clap ArgGroup).

The positional params and its `-` stdin form are unchanged. A missing file
surfaces "could not read params file '<path>': ...".

Tests: file happy path, missing-file error, positional/--params-file conflict.
Disabling Tooling Access cuts off blockchain access for all Quicknode
developer tooling on the account, so route it through confirm_mild
(Severity::Mild): --yes skips, TTY prompts, non-TTY without --yes returns
exit 5. Prompt names the blast radius.

Also set explicit value_name on 'qn rpc call' positionals (METHOD, PARAMS)
per the CLI value-name convention.

Tests: disable now needs --yes (exit 0); no --yes non-TTY returns exit 5
and reaches the mock zero times.
@johnpmitsch johnpmitsch merged commit e1cd987 into main Jul 10, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants