Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 15 additions & 87 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ name = "qn"
path = "src/lib.rs"

[dependencies]
quicknode-sdk = "0.6"
quicknode-sdk = "0.7"
clap = { version = "4", features = ["derive", "env", "wrap_help"] }
clap_complete = "4"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
Expand All @@ -39,6 +39,9 @@ time = { version = "0.3", features = ["formatting", "parsing", "macros"] }
base64 = "0.22"
tempfile = "3"
url = "2"
# Stable fingerprint of the API key for scoping the JWT token cache to an
# account. The key itself is never written to the cache — only this hash.
sha2 = "0.10"

[dev-dependencies]
reqwest = { version = "0.12", default-features = false }
Expand Down
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,55 @@ qn sql schema hyperliquid-core-mainnet
Queries are read-only (SELECT) and capped at 1000 rows per request; page through
larger result sets with `LIMIT`/`OFFSET` in the SQL.

### On-chain RPC

Make JSON-RPC calls with no endpoint to provision. `qn rpc call` mints and
refreshes a short-lived session JWT automatically; the only one-time step is
enabling Tooling Access (or pass `--yes` to enable on first use).

```sh
qn tooling-access enable # one-time; idempotent, requires an admin role
qn tooling-access status

qn rpc call eth_blockNumber
qn rpc call eth_getBalance '["0xabc...", "latest"]'
qn rpc call eth_call '{"to":"0x..."}'
qn rpc call eth_call --params-file params.json # read params from a file (-f)
echo '[...]' | qn rpc call eth_call - # read params from stdin

qn rpc call eth_blockNumber --yes # auto-enable Tooling Access if needed

# Multichain: the endpoint serves many chains. Target one by its network key.
qn rpc list-networks # list available network keys (alias: ls)
qn rpc call getSlot --network solana-mainnet
qn rpc call eth_chainId --network polygon

# Custom endpoint: send the call to a fully-formed HTTP URL instead of Tooling
# Access. The URL is self-authenticating (no session token is minted or sent).
qn rpc call eth_blockNumber --endpoint-url https://my-endpoint.example/rpc
```

Set a default custom endpoint in `~/.config/qn/config.toml` so every `qn rpc call`
uses it without the flag (a per-call `--endpoint-url` still overrides it):

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

`--endpoint-url` and `--network` are mutually exclusive: a custom URL is not
multichain-routed.

The network map is cached in `~/.config/qn/networks.toml` (per endpoint, 24h TTL),
so `--network` calls reuse it without re-fetching. Network keys are the endpoint's
own `multichain_urls` keys (note these can differ from chain slugs, e.g. `polygon`
not `matic`); `qn rpc list-networks` shows the exact set.

The session token is cached under `~/.config/qn/tokens.toml` (0600), scoped to the
API key, so subsequent calls skip the mint round trip while it's valid. Results are
schemaless JSON; `-o json|yaml|toon` controls the format (`table`/`md` fall back to
JSON).

### Other

```sh
Expand Down
20 changes: 20 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ pub struct Cli {
#[arg(long, global = true, hide = true)]
pub base_url: Option<String>,

/// Path prefix inserted between the host and each sub-client's path (e.g.
/// `/console-api`). For reverse-proxy / gateway environments. Requires
/// `--base-url`.
#[arg(long, global = true, hide = true)]
pub base_prefix: Option<String>,

/// Print help (see a summary with '-h').
#[arg(short = 'h', long, global = true, action = ArgAction::Help)]
pub help: Option<bool>,
Expand Down Expand Up @@ -149,6 +155,13 @@ pub enum Command {
/// Run SQL queries and inspect cluster schemas.
Sql(commands::sql::Args),

/// Make JSON-RPC calls against your Tooling Access endpoint.
Rpc(commands::rpc::Args),

/// Manage Tooling Access (the endpoint `qn rpc` uses).
#[command(name = "tooling-access")]
ToolingAccess(commands::tooling_access::Args),

/// Generate shell completion scripts.
///
/// When installing qn through a package manager, it's possible that no
Expand Down Expand Up @@ -206,6 +219,7 @@ impl Cli {
yes_count: self.yes,
retries: self.retries,
base_url: self.base_url.clone(),
base_prefix: self.base_prefix.clone(),
}
}

Expand Down Expand Up @@ -239,6 +253,12 @@ impl Cli {
Command::Webhook(args) => commands::webhook::run(args, Ctx::from_global(global)?).await,
Command::Kv(args) => commands::kv::run(args, Ctx::from_global(global)?).await,
Command::Sql(args) => commands::sql::run(args, Ctx::from_global(global)?).await,
// rpc builds its own Ctx (it seeds the SDK from the on-disk token
// cache before construction), so it takes `global` directly.
Command::Rpc(args) => commands::rpc::run(args, global).await,
Command::ToolingAccess(args) => {
commands::tooling_access::run(args, Ctx::from_global(global)?).await
}
}
}
}
26 changes: 26 additions & 0 deletions src/commands/agent/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ Top-level nouns (plurals like `endpoints`/`streams` and `ls` are accepted aliase
- `kv` — `set` (put, get, list, delete, bulk) and `list` (list, get, create, append,
contains, remove-item, update, delete)
- `sql` — query (inline SQL, `--file <path>`, or `--file -` for stdin), schema
- `tooling-access` — status, enable, disable (provisions the endpoint `rpc` uses)
- `rpc` — make JSON-RPC calls. `qn rpc call <method> [json-params]` calls the
account's Tooling Access endpoint (params is a JSON array or object inline, or
`--params-file <PATH>` / `-f` to read from a file, or `-` for stdin); the
session JWT is minted and refreshed automatically. On a
not-yet-enabled account it auto-enables with `--yes` (or prompts on a TTY).
Multichain: `--network <key>` targets a specific chain by its key (e.g.
`solana-mainnet`, `polygon`); `qn rpc list-networks` (alias `ls`) lists the
available keys. Custom endpoint: `--endpoint-url <URL>` (or `[rpc] endpoint_url`
in config) sends the call to a fully-formed HTTP URL that authenticates itself
(no token minted); it's mutually exclusive with `--network`.

Drill into any level with `--help`: `qn endpoint --help`, `qn endpoint security --help`,
`qn endpoint rate-limit --help`. Shell completions: `qn completions <bash|zsh|fish|...>`.
Expand Down Expand Up @@ -171,6 +182,21 @@ qn kv set get my-key
qn kv set list
```

**Make on-chain calls (no endpoint to provision):**

```sh
qn tooling-access enable --yes # one-time; idempotent, admin role required
qn rpc call eth_blockNumber # → "0x…" (default network)
qn rpc call eth_getBalance '["0xabc...", "latest"]'
qn rpc list-networks # available network keys for this endpoint
qn rpc call getSlot --network solana-mainnet
qn rpc call eth_blockNumber --endpoint-url https://my-endpoint.example/rpc
```

`qn rpc call` mints and refreshes the session JWT for you; the only one-time step
is enabling Tooling Access (or pass `--yes` to enable on first use). A custom
`--endpoint-url` (or `[rpc] endpoint_url` in config) bypasses that entirely.

## 8. Gotchas & safety rails

- Mutations are never retried; re-running a failed create can double-provision (§5).
Expand Down
2 changes: 2 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ pub mod chain;
pub mod endpoint;
pub mod kv;
pub mod metrics;
pub mod rpc;
pub mod sql;
pub mod stream;
pub mod team;
pub mod tooling_access;
pub mod usage;
pub mod webhook;
Loading
Loading