diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d9547e2..4e4b44a 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,9 +11,9 @@ }, "plugins": [ { - "name": "mcp", - "description": "Manage your blockchain infrastructure across 80+ chains with your agents.", - "source": "./plugins/mcp", + "name": "quicknode", + "description": "Build on Quicknode blockchain infrastructure: manage endpoints across 80+ chains (MCP), plus commands, a subagent, and a skill for RPC, Solana, Hyperliquid, x402, MPP, and more.", + "source": "./plugins/quicknode", "strict": false } ] diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 6f78d54..3486aae 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -11,8 +11,8 @@ }, "plugins": [ { - "name": "mcp", - "source": "./plugins/mcp", + "name": "quicknode", + "source": "./plugins/quicknode", "description": "Manage your blockchain infrastructure across 80+ chains with your agents." } ] diff --git a/.github/workflows/publish-mcp-registry.yml b/.github/workflows/publish-mcp-registry.yml index e186aea..c2bf12d 100644 --- a/.github/workflows/publish-mcp-registry.yml +++ b/.github/workflows/publish-mcp-registry.yml @@ -22,12 +22,12 @@ jobs: - name: Sync version from tag to server.json run: | VERSION="${GITHUB_REF#refs/tags/v}" - jq --arg v "$VERSION" '.version = $v' plugins/mcp/server.json > plugins/mcp/server.tmp.json - mv plugins/mcp/server.tmp.json plugins/mcp/server.json - cat plugins/mcp/server.json + jq --arg v "$VERSION" '.version = $v' plugins/quicknode/server.json > plugins/quicknode/server.tmp.json + mv plugins/quicknode/server.tmp.json plugins/quicknode/server.json + cat plugins/quicknode/server.json - name: Authenticate to MCP Registry via GitHub OIDC run: ./mcp-publisher login github-oidc - name: Publish server to MCP Registry - run: ./mcp-publisher publish plugins/mcp/server.json + run: ./mcp-publisher publish plugins/quicknode/server.json diff --git a/.github/workflows/sync-skill.yml b/.github/workflows/sync-skill.yml new file mode 100644 index 0000000..58cdc96 --- /dev/null +++ b/.github/workflows/sync-skill.yml @@ -0,0 +1,58 @@ +name: Sync quicknode-skill + +on: + schedule: + - cron: '0 9 * * 1' # Every Monday 09:00 UTC + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + sync: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run sync script + id: sync + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + bash scripts/sync-skill.sh + EXIT=$? + set -e + if [ $EXIT -eq 1 ]; then + echo "drift=true" >> "$GITHUB_OUTPUT" + else + echo "drift=false" >> "$GITHUB_OUTPUT" + fi + + - name: Read upstream SHA + id: meta + if: steps.sync.outputs.drift == 'true' + run: | + SHA=$(grep "Upstream commit:" plugins/quicknode/skills/quicknode-skill/SKILL_SOURCE.md | awk '{print $NF}') + echo "sha=${SHA}" >> "$GITHUB_OUTPUT" + + - name: Open PR on drift + if: steps.sync.outputs.drift == 'true' + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: sync/quicknode-skill-${{ steps.meta.outputs.sha }} + delete-branch: true + commit-message: "sync: update quicknode-skill from blockchain-skills@${{ steps.meta.outputs.sha }}" + title: "Sync quicknode-skill from blockchain-skills@${{ steps.meta.outputs.sha }}" + body: | + Automated sync of `plugins/quicknode/skills/quicknode-skill/` from upstream [`quiknode-labs/blockchain-skills`](https://github.com/quiknode-labs/blockchain-skills). + + **Upstream commit:** `${{ steps.meta.outputs.sha }}` + + Review the diff to confirm no breaking changes before merging. + labels: "sync,automated" + reviewers: "0xsergen" diff --git a/README.md b/README.md index 9e1b166..555481c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Agent plugins from Quicknode. MCP servers, skills, and more. ## Available plugins -| Plugin | Description | -| ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| [`mcp`](./plugins/mcp/) | Manage your blockchain infrastructure across 80+ chains with your agents. | +| Plugin | Description | +| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`quicknode`](./plugins/quicknode/) | Build on Quicknode blockchain infrastructure: manage endpoints across 80+ chains (MCP), plus commands, a subagent, and a skill for RPC, Solana, Hyperliquid, x402, MPP, and more. | ## Install @@ -17,7 +17,13 @@ Agent plugins from Quicknode. MCP servers, skills, and more. | VS Code | [docs/install/vscode.md](./docs/install/vscode.md) | | Zed | [docs/install/zed.md](./docs/install/zed.md) | -For Claude Code and ChatGPT, use the existing listings on the respective marketplaces. +For Claude Code, add this marketplace and install the `quicknode` plugin: + +``` +/plugin marketplace add quicknode/agent-plugins +``` + +For ChatGPT, use the existing listing on its marketplace. ## License diff --git a/docs/install/cursor.md b/docs/install/cursor.md index 26db2df..275c850 100644 --- a/docs/install/cursor.md +++ b/docs/install/cursor.md @@ -31,7 +31,7 @@ No `auth` block needed. The Quicknode MCP server uses OAuth 2.1 with **Dynamic C ## What you get -Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/mcp/README.md) for capabilities. +Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/quicknode/README.md) for capabilities. ## Troubleshooting diff --git a/docs/install/vscode.md b/docs/install/vscode.md index 8fb010f..3bd3b16 100644 --- a/docs/install/vscode.md +++ b/docs/install/vscode.md @@ -37,7 +37,7 @@ On first connection, VS Code performs OAuth 2.1 + Dynamic Client Registration ag ## What you get -Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/mcp/README.md) for capabilities. +Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/quicknode/README.md) for capabilities. ## Requirements diff --git a/docs/install/windsurf.md b/docs/install/windsurf.md index 3f95b36..a141cc5 100644 --- a/docs/install/windsurf.md +++ b/docs/install/windsurf.md @@ -32,7 +32,7 @@ windsurf://windsurf-mcp-registry?serverName=quicknode-mcp ## What you get -Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/mcp/README.md) for capabilities. +Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/quicknode/README.md) for capabilities. ## Requirements diff --git a/docs/install/zed.md b/docs/install/zed.md index 473652f..b4c0063 100644 --- a/docs/install/zed.md +++ b/docs/install/zed.md @@ -39,7 +39,7 @@ Note: Zed uses `context_servers` (not `mcpServers`). ## What you get -Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/mcp/README.md) for capabilities. +Manage your blockchain infrastructure from your AI assistant: endpoints, rate limits, security, metrics, logs, and billing. See the [plugin README](../../plugins/quicknode/README.md) for capabilities. ## Requirements diff --git a/plugins/mcp/README.md b/plugins/mcp/README.md deleted file mode 100644 index 12644b7..0000000 --- a/plugins/mcp/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Quicknode MCP - -Give your AI agents the best blockchain infrastructure. - -- **Endpoint**: `https://mcp.quicknode.com/mcp` -- **Transport**: Streamable HTTP (stateless) -- **Auth**: OAuth 2.1 with Dynamic Client Registration (RFC 7591). Clients register themselves automatically; no API key in your config. - -## Install - -See per-client guides at the repo root: - -- [Cursor](../../docs/install/cursor.md) -- [Windsurf](../../docs/install/windsurf.md) -- [VS Code](../../docs/install/vscode.md) -- [Zed](../../docs/install/zed.md) - -Manual config (works for any client supporting remote MCP): - -```json -{ - "mcpServers": { - "quicknode": { - "type": "http", - "url": "https://mcp.quicknode.com/mcp" - } - } -} -``` - -On first connection, the client performs DCR against `https://mcp.quicknode.com/register`, then walks you through OAuth in your browser. No pre-shared `CLIENT_ID` / `CLIENT_SECRET` needed. - -## What you can do - -- **Endpoint management**: list, inspect, provision, and archive Quicknode endpoints across supported chains. -- **Rate limits**: adjust general (RPS/RPM/RPD) limits and configure per-method rate limiters. -- **Security**: manage endpoint security options (CORS, JWT, IPs, etc.) and rules (IP, JWT, referrer, domain mask, token). -- **Observability**: fetch endpoint metrics, request/response logs, and account-level RPC usage breakdowns. -- **Account**: query billing and discover supported chains. - -Once connected, your MCP client will display the live tool list from the server. - -## Requirements - -A Quicknode account. Sign up at [quicknode.com](https://www.quicknode.com). - -## License - -MIT. See [LICENSE.md](../../LICENSE.md) at the repo root. diff --git a/plugins/quicknode/.claude-plugin/plugin.json b/plugins/quicknode/.claude-plugin/plugin.json new file mode 100644 index 0000000..8240fd9 --- /dev/null +++ b/plugins/quicknode/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "quicknode", + "description": "Build on Quicknode blockchain infrastructure from your AI coding agent. Manage endpoints, streams, webhooks, and 80+ chains — plus accurate API knowledge for RPC, Solana, Hyperliquid, x402, MPP, and more.", + "version": "1.0.0", + "author": { + "name": "Quicknode", + "url": "https://www.quicknode.com" + }, + "homepage": "https://www.quicknode.com/docs/build-with-ai", + "repository": "https://github.com/quicknode/agent-plugins", + "license": "MIT" +} diff --git a/plugins/mcp/.cursor-plugin/plugin.json b/plugins/quicknode/.cursor-plugin/plugin.json similarity index 93% rename from plugins/mcp/.cursor-plugin/plugin.json rename to plugins/quicknode/.cursor-plugin/plugin.json index eb17a66..6fdf91f 100644 --- a/plugins/mcp/.cursor-plugin/plugin.json +++ b/plugins/quicknode/.cursor-plugin/plugin.json @@ -1,5 +1,5 @@ { - "name": "mcp", + "name": "quicknode", "displayName": "Quicknode MCP", "version": "1.0.0", "description": "Manage your blockchain infrastructure across 80+ chains with your agents.", diff --git a/plugins/quicknode/README.md b/plugins/quicknode/README.md new file mode 100644 index 0000000..616d149 --- /dev/null +++ b/plugins/quicknode/README.md @@ -0,0 +1,73 @@ +# Quicknode + +Build on Quicknode blockchain infrastructure from your AI agent. + +In **Claude Code**, this plugin bundles the Quicknode MCP server, outcome-focused slash commands, an RPC troubleshooting subagent, and a continuously-synced blockchain skill. In other clients (Cursor, Windsurf, VS Code, Zed), it provides the remote MCP server. + +## MCP server + +- **Endpoint**: `https://mcp.quicknode.com/mcp` +- **Transport**: Streamable HTTP (stateless) +- **Auth**: OAuth 2.1 with Dynamic Client Registration (RFC 7591). Clients register themselves automatically; no API key in your config. + +Manage your blockchain infrastructure: list, inspect, provision, and archive endpoints across supported chains; adjust rate limits; manage security options and rules; fetch metrics, logs, and usage; and query billing and supported chains. + +## Slash commands (Claude Code) + +Outcome-focused commands that route you to the right Quicknode product. Namespaced as `/quicknode:`. + +| Command | What it does | +| ----------------------- | ------------------------------------------------------------------------------------------ | +| `/quicknode:build-web3` | Scaffold a working Web3 starter app wired to the right Quicknode product for your use case. | +| `/quicknode:new-endpoint` | Provision a Quicknode RPC endpoint (chain/network/name) via MCP. | +| `/quicknode:monitor` | Set up real-time on-chain event monitoring — picks Webhooks vs Streams for your needs. | +| `/quicknode:backfill` | Configure a Streams Dataset job to retrieve historical blockchain data over a range. | +| `/quicknode:query-data` | Write and run SQL against Quicknode SQL Explorer. | +| `/quicknode:swap` | Get a quote or execute a token swap on Solana or EVM via the Swap API. | +| `/quicknode:agent-access` | Add wallet-paid blockchain access to an AI agent via x402 or MPP. | + +## Subagent (Claude Code) + +- **rpc-troubleshooter** — a Quicknode RPC diagnostics specialist. Triggers on RPC errors (reverts, 429s, timeouts, missing methods, chain mismatches, archive/trie-node errors) and returns a root-cause diagnosis, a corrected snippet, and any relevant add-on/plan recommendation. + +## Skill (Claude Code) + +- **quicknode-skill** — accurate API knowledge across 80+ chains: RPC, Streams, Webhooks, SQL Explorer, IPFS, Solana DAS, Yellowstone gRPC, HyperCore, KV Store, Admin API, x402, MPP, and Agent Subscriptions. Synced automatically from [`quiknode-labs/blockchain-skills`](https://github.com/quiknode-labs/blockchain-skills) via a weekly GitHub workflow, so its API details stay current. + +## Install + +**Claude Code** — add the marketplace, then install the `quicknode` plugin: + +``` +/plugin marketplace add quicknode/agent-plugins +``` + +**Other clients** — see the per-client guides at the repo root: + +- [Cursor](../../docs/install/cursor.md) +- [Windsurf](../../docs/install/windsurf.md) +- [VS Code](../../docs/install/vscode.md) +- [Zed](../../docs/install/zed.md) + +Manual MCP config (works for any client supporting remote MCP): + +```json +{ + "mcpServers": { + "quicknode": { + "type": "http", + "url": "https://mcp.quicknode.com/mcp" + } + } +} +``` + +On first connection, the client performs DCR against `https://mcp.quicknode.com/register`, then walks you through OAuth in your browser. No pre-shared `CLIENT_ID` / `CLIENT_SECRET` needed. + +## Requirements + +A Quicknode account. Sign up at [quicknode.com](https://www.quicknode.com). + +## License + +MIT. See [LICENSE.md](../../LICENSE.md) at the repo root. diff --git a/plugins/quicknode/agents/rpc-troubleshooter.md b/plugins/quicknode/agents/rpc-troubleshooter.md new file mode 100644 index 0000000..3788398 --- /dev/null +++ b/plugins/quicknode/agents/rpc-troubleshooter.md @@ -0,0 +1,48 @@ +--- +description: Diagnose Quicknode RPC errors — reverts, timeouts, rate limits, wrong network, missing methods. Triggers on phrases like "RPC error", "eth_call reverted", "429 from Quicknode", "JSON-RPC timeout", "execution reverted", "missing trie node", "wrong chainId". +tools: Read, Grep, WebFetch +--- + +You are a Quicknode RPC diagnostics specialist. Your only job is to diagnose RPC errors and suggest a concrete fix or the right Quicknode add-on. + +## Step 1 — Gather the error + +Ask the user (or extract from context) for: + +1. The **exact error message** (copy-paste the full JSON-RPC error or HTTP response) +2. The **method** that failed (e.g. `eth_call`, `trace_transaction`, `eth_getLogs`) +3. The **chain and network** (Ethereum mainnet? Base? Solana?) +4. The **endpoint URL** (redact the key — just the host is enough) +5. The **request payload** if available (contract address, block number, params) + +## Step 2 — Diagnose + +Cross-reference `skills/quicknode-skill/references/rpc-reference.md` and `skills/quicknode-skill/references/marketplace-addons.md`. + +Map the error to one of these root causes: + +| Symptom | Root cause | Fix | +|---------|-----------|-----| +| `execution reverted` / `revert` in data | Contract-level revert — not an RPC issue | Decode revert reason; check contract state, params, msg.sender | +| `429 Too Many Requests` | Rate limit exceeded | Check endpoint rate limit in dashboard; upgrade plan or add rate limit exception | +| `method not found` / `-32601` | Method not enabled on the endpoint | Enable the method in endpoint settings or add the relevant add-on | +| `timeout` / no response | Network or compute timeout | Check if the request is too compute-heavy (large `eth_getLogs` range); add block range limit | +| `wrong chainId` / chain mismatch | Endpoint URL points to wrong chain | Verify endpoint chain/network in dashboard | +| `missing trie node` / `state not available` | Archive data requested on non-archive endpoint | Switch to an archive endpoint or add Archive add-on | +| `invalid JSON` / `-32700` | Malformed request payload | Show corrected request format | +| `ECONNREFUSED` / `ENOTFOUND` | Endpoint URL wrong or service down | Verify URL; check status.quicknode.com | + +## Step 3 — Recommend + +Based on the diagnosis: + +1. **Immediate fix** — What the user can change right now (request params, block range, endpoint setting) +2. **Add-on or plan upgrade** (if needed) — Reference `marketplace-addons.md` for the exact add-on name and what it enables +3. **Code fix** (if applicable) — Show the corrected request snippet + +## Rules + +- Never guess the root cause without evidence from the error message +- If the error is a contract revert, say so clearly — it is not a Quicknode issue +- Only suggest add-ons that exist in `marketplace-addons.md` — do not invent product names +- Keep the diagnosis to 3 sections: what happened, why, and how to fix it diff --git a/plugins/quicknode/commands/agent-access.md b/plugins/quicknode/commands/agent-access.md new file mode 100644 index 0000000..b6ba0d0 --- /dev/null +++ b/plugins/quicknode/commands/agent-access.md @@ -0,0 +1,37 @@ +--- +description: Add wallet-paid blockchain access to an AI agent — no API keys, pay with stablecoins via x402 or MPP. Optionally provision a full Quicknode account via Agent Subscriptions. +argument-hint: "[x402|mpp] [one-shot|full-account]" +--- + +You are a Quicknode agentic payments expert. Help the user add wallet-paid blockchain access to an AI agent using x402 or MPP. + +Read `skills/quicknode-skill/references/x402-reference.md` and `skills/quicknode-skill/references/mpp-reference.md` for accurate protocol details, pricing, endpoint URLs, SDK names, and header formats before responding. Do not use any values from memory — always derive them from the reference files. + +## Step 1 — Pick the access mode + +Ask if not clear from context: + +- **One-shot RPC** — Agent pays per request directly via the protocol proxy. No Quicknode account needed. +- **Full platform account** — Agent uses the same protocols against Agent Subscriptions to provision a full Quicknode API key, unlocking RPC, Streams, Webhooks, SQL Explorer, Key-Value Store, and Admin API. + +## Step 2 — Pick the protocol + +From the reference files, summarize the key tradeoffs between x402 and MPP (billing model, session support, SDK availability) in a compact table, then ask the user which fits their use case — or recommend one based on their context. + +## Step 3 — Setup walkthrough + +Once the user has chosen protocol + access mode: + +1. Show the correct base URL for the chosen protocol (from the reference) +2. Show the install command for the official SDK +3. Produce a working TypeScript snippet that handles the full payment flow — auth, headers, and error handling — using the SDK (no manual header construction) +4. Provide a `.env.example` with the required env vars (no values, just keys) + +If the user chose **full platform account**: show the Agent Subscriptions flow — use the chosen protocol to provision a `QN_*` API key, then use it like a normal Quicknode account. + +## Rules + +- All pricing, URLs, and header formats must come from the reference files — never from memory +- Always use environment variables for private keys — never hard-code +- If the user is building on Solana, check the reference for the Solana-specific SDK variant +- Do not invent header formats — use only what the reference files document diff --git a/plugins/quicknode/commands/backfill.md b/plugins/quicknode/commands/backfill.md new file mode 100644 index 0000000..40b2349 --- /dev/null +++ b/plugins/quicknode/commands/backfill.md @@ -0,0 +1,41 @@ +--- +description: Retrieve historical blockchain data for a date range or block range — configure a Quicknode Streams Dataset (backfill job). +argument-hint: "" +--- + +You are a Quicknode Streams backfill expert. Help the user retrieve historical blockchain data by configuring a Streams Dataset job. + +Read `skills/quicknode-skill/references/streams-reference.md` for accurate Dataset configuration syntax and destination options before responding. + +## Step 1 — Define the job + +Ask (in one message if not already in the argument): + +1. **What data** — e.g. "all ERC-20 transfers for USDC on Ethereum", "every transaction touching address 0x...", "Solana SPL token transfers" +2. **Time range** — Start date/block and end date/block. For open-ended ("from genesis"), confirm — this can produce very large datasets +3. **Destination** — Where should the data land? Options: S3, GCS, Azure Blob, Postgres, webhook, Snowflake, Kafka +4. **Chain + network** +5. **Format** — JSON or Parquet (Parquet recommended for large datasets) + +## Step 2 — Estimate volume + +Before producing config, give a rough volume callout: +- Ethereum full transaction history is billions of rows — filtered jobs (single address, specific event) are tractable; unfiltered chain-wide jobs are not +- For large date ranges, suggest Parquet + S3/GCS and a columnar query tool on top + +## Step 3 — Produce the configuration + +1. **Filter function** (if applicable) — JavaScript filter to narrow the dataset +2. **Dataset job config** — fields: `network`, `startBlock` / `startDate`, `endBlock` / `endDate`, `dataset`, `destination` +3. **Creation snippet** — `curl` against the Admin API or SDK snippet +4. **What to do once done** — e.g. "query the Parquet files with DuckDB" or "your Postgres table will be populated incrementally" + +## Quicknode SDK and CLI + +Streams Datasets have no MCP coverage yet — for code and agents, the Quicknode SDK is the primary way to create and manage backfill jobs. Read `skills/quicknode-skill/references/sdk-reference.md` for accurate method names, supported languages, and examples. The Quicknode CLI covers the same for terminal and CI use. For automated or recurring data pipelines, always lead with the SDK rather than a one-off API call. + +## Rules + +- Use block numbers when the user gives dates — show the conversion (`eth_getBlockByTime` or equivalent) +- Do not start a backfill job without confirming the date range and destination with the user first +- If they need ongoing real-time data after the backfill, suggest `/quicknode:monitor` to add a live Stream on top diff --git a/plugins/quicknode/commands/build-web3.md b/plugins/quicknode/commands/build-web3.md new file mode 100644 index 0000000..9a46eb2 --- /dev/null +++ b/plugins/quicknode/commands/build-web3.md @@ -0,0 +1,47 @@ +--- +description: Scaffold a Web3 app wired to Quicknode infrastructure — pick a chain, pick a use case, get a working starter. +argument-hint: "[chain] [use-case]" +--- + +You are a Quicknode-aware Web3 scaffolding assistant. Your job is to help the user scaffold a working Web3 application wired to Quicknode infrastructure. + +## Intake + +If the user didn't specify a chain and use case, ask them now (one question at a time): + +1. **Chain** — Which chain? (e.g. Ethereum, Solana, Base, Arbitrum, Polygon, BNB, Avalanche) +2. **Use case** — What are they building? (e.g. NFT minting, token transfers, DeFi protocol, wallet app, trading bot, agent with payments, real-time event monitoring, historical data analytics) +3. **Stack** — Frontend framework and language preference (e.g. Next.js/TypeScript, React/JS, Node.js script, Python, Rust) + +## Scaffolding + +Once you have chain + use case + stack: + +1. Outline the architecture in 3–5 bullet points — what the app does, which Quicknode products it uses, and why. Match the product to the use case: + - Real-time events or alerts → Streams or Webhooks (`/quicknode:monitor`) + - Historical data queries → SQL Explorer (`/quicknode:query-data`) + - Historical data export / large datasets → Streams Backfill (`/quicknode:backfill`) + - Token swaps → Swap API (`/quicknode:swap`) + - Keyless agent payments → x402 or MPP (`/quicknode:agent-access`) + - Anything else → RPC endpoint (`/quicknode:new-endpoint`) +2. Generate a minimal working starter: + - A Quicknode endpoint placeholder (`process.env.QUICKNODE_ENDPOINT`) wired to the right RPC call + - The core logic for the use case (transfer, mint, listen, swap, etc.) + - A `.env.example` with `QUICKNODE_ENDPOINT=https://your-endpoint.quicknode.pro/...` +3. Show the user how to create the endpoint: "Run `/quicknode:new-endpoint` to create one, or visit [dashboard.quicknode.com](https://dashboard.quicknode.com)" + +## Accuracy rules + +- Use the skill knowledge for Quicknode-specific APIs (Streams, Swap API, x402, etc.) +- Use `ethers.js` v6 or `viem` for EVM, `@solana/kit` for Solana unless the user specifies otherwise — these are blockchain interaction libraries +- The **Quicknode SDK** is distinct: it manages Quicknode products (Admin API, Streams, Webhooks, KV Store, SQL Explorer) from code. If the app needs to manage Quicknode infrastructure programmatically, suggest the SDK alongside the blockchain library. The skill has details. +- Never hard-code API keys or endpoints — always use environment variables +- Keep the scaffold minimal: one working entry point, no boilerplate pages or test suites unless asked + +## Output format + +Deliver in this order: +1. Architecture summary (bullets) +2. Code files (labeled with filename) +3. Setup instructions (numbered, ≤5 steps) +4. Next steps the user can take — reference the relevant command by name (e.g. "Add `/quicknode:monitor` to watch for on-chain events", "Use `/quicknode:query-data` to analyze historical activity") diff --git a/plugins/quicknode/commands/monitor.md b/plugins/quicknode/commands/monitor.md new file mode 100644 index 0000000..a586cdb --- /dev/null +++ b/plugins/quicknode/commands/monitor.md @@ -0,0 +1,49 @@ +--- +description: Watch for on-chain events in real time — get alerts or pipe data to your destination. Chooses between Webhooks (simple) and Streams (complex) based on your needs. +argument-hint: "" +--- + +You are a Quicknode event monitoring expert. Help the user get notified about on-chain events using the right tool for their situation. + +Read `skills/quicknode-skill/references/streams-reference.md` and `skills/quicknode-skill/references/webhooks-reference.md` before responding. + +## Step 1 — Understand the outcome + +Ask (in one message if not already in the argument): + +1. **What event** — e.g. "USDC transfers above $10k", "any transaction to my contract address", "Solana account balance changes" +2. **What to do with it** — e.g. "call my webhook", "send to Postgres", "fan out to Kafka", "write to S3" +3. **Historical data needed?** — Do they need past events too, or only from now on? (If yes, suggest `/quicknode:backfill` for the historical portion) +4. **Chain + network** + +## Step 2 — Pick the right tool + +| Situation | Use | +|-----------|-----| +| Single destination (webhook URL), address/event filter, no transformation | **Webhooks** — simpler setup, no filter code | +| Multiple destinations, custom JS filter, high volume, or need backfill | **Streams** — more config, full control | + +Explain the choice in one sentence before proceeding. + +## Step 3 — Configure + +**If Webhooks:** +1. Address(es) or event types to watch +2. The destination webhook URL +3. Show a `curl` command or SDK snippet to create the webhook + +**If Streams:** +1. Filter function (JavaScript) based on their event description — use the exact syntax from `streams-reference.md` +2. Destination config (endpoint, auth, format) +3. Stream creation snippet (`curl` or SDK) +4. Warn if the filter is very broad (e.g. all txns on mainnet) — suggest narrowing + +## Quicknode SDK and CLI + +Streams and Webhooks have no MCP coverage yet — for code and agents, the Quicknode SDK is the primary way to create and manage them. Read `skills/quicknode-skill/references/sdk-reference.md` for accurate method names, supported languages, and examples. The Quicknode CLI covers the same surface for terminal and CI use. When the user needs Stream or Webhook creation inside an application, service, or AI agent, lead with the SDK rather than a one-off API call. + +## Rules + +- Do not invent filter operators — use only syntax from the reference files +- If the user needs historical data, tell them clearly: Streams Datasets handle backfill; Webhooks do not +- Always confirm chain + network before producing any config diff --git a/plugins/quicknode/commands/new-endpoint.md b/plugins/quicknode/commands/new-endpoint.md new file mode 100644 index 0000000..11ce70f --- /dev/null +++ b/plugins/quicknode/commands/new-endpoint.md @@ -0,0 +1,48 @@ +--- +description: Create a Quicknode RPC endpoint — guided chain selection, network, and options via the Quicknode MCP. +argument-hint: "[chain] [network]" +--- + +You are a Quicknode endpoint provisioning assistant. Help the user create a new RPC endpoint using the Quicknode MCP tools. + +## Step 1 — Gather inputs + +If chain or network weren't provided as arguments, ask: + +1. **Chain** — Which blockchain? Call `list-chains` (MCP) and present the options grouped by ecosystem (EVM / Solana / Other). +2. **Network** — Mainnet or a testnet? List available networks for the chosen chain. +3. **Name** (optional) — A friendly name for the endpoint (default: the chain + network slug). + +## Step 2 — Create the endpoint + +Call the `create-endpoint` MCP tool with the gathered inputs. Present the result clearly: + +``` +Endpoint created: + Name: + Chain: + Network: + URL: + HTTP: + WSS: +``` + +## Step 3 — Next steps + +Offer the user relevant follow-up actions based on the chain: + +- "Add this endpoint to your project: `QUICKNODE_ENDPOINT=`" +- "Set rate limits: 'limit this endpoint to 100 req/s'" +- "Enable security rules: 'add an allowlist to this endpoint'" +- For EVM chains: "Test it: `cast block-number --rpc-url `" +- For Solana: "Test it: `solana config set --url && solana block-height`" + +## Quicknode SDK and CLI + +The Quicknode SDK and Quicknode CLI both cover endpoint management via the Admin API — creating, listing, updating, and deleting endpoints without the MCP. If the user wants to automate endpoint provisioning inside an application, script, or CI pipeline, suggest the SDK. For one-off terminal management, suggest the CLI. The skill has details on both. + +## Rules + +- Never proceed with creation without confirming chain + network with the user first +- If `create-endpoint` fails, surface the MCP error verbatim and suggest checking account permissions +- Do not fabricate endpoint URLs — only show what the MCP tool returns diff --git a/plugins/quicknode/commands/query-data.md b/plugins/quicknode/commands/query-data.md new file mode 100644 index 0000000..43d36c1 --- /dev/null +++ b/plugins/quicknode/commands/query-data.md @@ -0,0 +1,46 @@ +--- +description: Query Hyperliquid trading data with SQL — write, optimize, and run queries against Quicknode SQL Explorer. +argument-hint: "" +--- + +You are a Quicknode SQL Explorer expert. Help the user write and run SQL queries against indexed Hyperliquid (HyperCore) trading data. + +Read `skills/quicknode-skill/references/sql-explorer.md` for accurate table schemas, available columns, and API details before responding. + +## Scope + +SQL Explorer currently supports one cluster: `hyperliquid-core-mainnet`. If the user is asking about a different chain, let them know and suggest Streams Backfill (`/quicknode:backfill`) as an alternative for other chains. + +## Step 1 — Understand the question + +If no argument was given, ask what they want to know. Frame options around what the data supports: +- Trade history (coin, side, price, size, fees, buyer/seller addresses) +- Volume analysis (by coin, by address, over time) +- Specific address activity +- Fee breakdowns + +## Step 2 — Identify the tables + +From `sql-explorer.md`, identify the relevant table(s) and show the user the key columns before writing the query. + +## Step 3 — Write the query + +Produce a well-formatted SQL query that: +- Uses correct table and column names from `sql-explorer.md` +- Scopes to a time range with a `WHERE block_time > now() - INTERVAL ...` clause +- Adds `LIMIT` on exploratory queries unless the user explicitly needs all rows +- Uses `clusterId: "hyperliquid-core-mainnet"` in the API call + +## Step 4 — Run and interpret + +Show the API call to execute the query — the endpoint is `https://api.quicknode.com/sql/rest/v1/query` with an `x-api-key` header. Interpret the result in 1–2 sentences and suggest a natural follow-up query if there is one. + +## Quicknode SDK and CLI + +SQL Explorer has no MCP coverage yet — for code and agents, the Quicknode SDK is the primary way to run queries and retrieve table schemas programmatically. Read `skills/quicknode-skill/references/sdk-reference.md` for accurate method names, supported languages, and examples. The Quicknode CLI covers the same for terminal use. When query results need to feed into an application or agent, lead with the SDK rather than a direct REST API call. + +## Rules + +- Only use tables and columns documented in `sql-explorer.md` — never guess schema +- Warn if a query has no time filter — unbounded queries on `hyperliquid_trades` can be very large +- If the user asks about a chain other than Hyperliquid, say so clearly and offer an alternative diff --git a/plugins/quicknode/commands/swap.md b/plugins/quicknode/commands/swap.md new file mode 100644 index 0000000..468422e --- /dev/null +++ b/plugins/quicknode/commands/swap.md @@ -0,0 +1,45 @@ +--- +description: Quote or execute a token swap on Solana or EVM chains via the Quicknode Swap API. +argument-hint: " to on " +--- + +You are a Quicknode Swap API expert. Help the user get a quote or execute a token swap using the Quicknode Swap API. + +Read `skills/quicknode-skill/references/swap-api-reference.md` for accurate endpoint paths, request/response shapes, and chain-specific details before responding. + +## Intake + +If not fully specified in the argument, ask: + +1. **From token** — Symbol or mint/contract address (e.g. USDC, SOL, ETH, 0x...) +2. **To token** — Symbol or mint/contract address +3. **Amount** — In human-readable units (e.g. "100 USDC", "0.5 SOL", "1 ETH") +4. **Chain** — Solana or an EVM chain (Ethereum, Base, Arbitrum, Polygon, etc.) +5. **Action** — Quote only, or execute the swap? (If execute: do they have a wallet ready?) +6. **Slippage** (optional) — Max slippage in basis points (default: 50 bps = 0.5%) + +## Quote + +Produce a ready-to-run snippet that calls the Quicknode Swap API `/quote` endpoint for the specified pair and amount. Show the user: + +- Expected output amount +- Price impact +- Route (aggregator path) +- Fee breakdown + +Use their Quicknode endpoint URL as `process.env.QUICKNODE_ENDPOINT`. + +## Execute + +If the user wants to execute: + +1. Show the swap transaction snippet (sign + send via wallet) +2. Remind them to verify the quote is fresh (quotes expire after ~30s) +3. Include slippage protection in the transaction + +## Rules + +- Use API paths and parameters exactly as documented in `swap-api-reference.md` +- Never execute a swap without showing the quote and asking for explicit user confirmation first +- Always use environment variables for endpoint URLs and private keys — never hard-code +- For EVM: use `viem` or `ethers.js` v6. For Solana: use `@solana/kit`. diff --git a/plugins/mcp/mcp.json b/plugins/quicknode/mcp.json similarity index 100% rename from plugins/mcp/mcp.json rename to plugins/quicknode/mcp.json diff --git a/plugins/mcp/server.json b/plugins/quicknode/server.json similarity index 92% rename from plugins/mcp/server.json rename to plugins/quicknode/server.json index afb0550..6fcf278 100644 --- a/plugins/mcp/server.json +++ b/plugins/quicknode/server.json @@ -6,7 +6,7 @@ "repository": { "url": "https://github.com/quicknode/agent-plugins", "source": "github", - "subfolder": "plugins/mcp" + "subfolder": "plugins/quicknode" }, "remotes": [ { diff --git a/plugins/quicknode/skills/quicknode-skill/SKILL.md b/plugins/quicknode/skills/quicknode-skill/SKILL.md new file mode 100644 index 0000000..66398e4 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/SKILL.md @@ -0,0 +1,812 @@ +--- +name: quicknode-skill +description: "Quicknode blockchain infrastructure: endpoint access for 80+ chains over RPC/WebSocket/gRPC/REST, Streams, Webhooks, SQL Explorer, IPFS, Solana DAS API, Yellowstone and Hypercore gRPC, Key-Value Store, Admin API, x402 and MPP pay-per-request RPC, and Agent Subscriptions. Use for any Quicknode product, qn_ methods, blockchain data access on Ethereum, Solana, Hyperliquid and other supported chains, or wallet-paid agent access (x402, MPP, agent subscription)." +--- + +# Quicknode Blockchain Infrastructure + +## Intake Questions +- Which chain and network should Quicknode target? +- Is this read-only or should I create infrastructure (streams, webhooks, IPFS writes)? +- Does this require real-time streaming (gRPC/Yellowstone/Hypercore) or standard RPC? +- What endpoint or API key should I use (default: `QUICKNODE_RPC_URL`, optional `QUICKNODE_WSS_URL` / `QUICKNODE_API_KEY`)? +- If no API key exists, does the agent want pay-per-request access (x402, MPP) or a wallet-paid Quicknode account via [Agent Subscriptions](#agent-subscriptions)? +- Any constraints (latency, regions, throughput, destinations)? + +## Safety Defaults +- Default to testnet/devnet when a network is not specified. +- Prefer read-only operations and dry runs before creating resources. +- Never ask for or accept private keys or secret keys. + +## Confirm Before Write +- Require explicit confirmation before creating or modifying Streams, Webhooks, or IPFS uploads. +- Require explicit confirmation before creating an Agent Subscription, topping up credits, or any action that spends real funds via x402 or MPP. +- If confirmation is missing, return the exact API payload for review. + +## Quick Reference + +| Product | Description | Use Case | +|---------|-------------|----------| +| **RPC Endpoints** | High-performance blockchain access | dApp backend, wallet interactions | +| **Streams** | Real-time & historical blockchain data pipelines | Event monitoring, analytics, indexing | +| **Webhooks** | Event-driven notifications | Alerts, transaction monitoring | +| **IPFS** | Decentralized file storage | NFT metadata, asset hosting | +| **Add-ons** | Enhanced blockchain APIs | Token balances, NFT data, DeFi | +| **DAS API** | Solana Digital Asset Standard (add-on) | NFT/token queries, compressed NFTs, asset search | +| **Yellowstone gRPC** | Solana Geyser streaming (add-on) | Real-time account, transaction, slot data | +| **Hypercore** | Hyperliquid gRPC/JSON-RPC/WS (beta) | Trades, orders, book updates, blocks, TWAP, events, writer actions | +| **SQL Explorer** | Direct SQL access to indexed blockchain data | Trading analytics, historical queries, market analysis | +| **Admin API** | REST API for account management | Endpoint CRUD, usage monitoring, billing | +| **Key-Value Store** | Serverless key-value and list storage (beta) | Persistent state for Streams, dynamic address lists | +| **x402** | Pay-per-request ($0.001/call) or credit drawdown ($10/1M) RPC via stablecoins | Keyless RPC access, AI agents, pay-as-you-go | +| **MPP** | Pay-per-request RPC via IETF Payment Authentication headers | AI agents, multi-service payments, high-volume sessions | +| **Agent Subscriptions** | Wallet-paid Quicknode account creation via x402 or MPP, returns a `QN_*` full platform API key | Autonomous agents that need full platform access without dashboard signup | + +## RPC Endpoints + +Quicknode provides low-latency RPC endpoints for 80+ blockchain networks. + +### Endpoint Setup + +```typescript +// EVM chains (ethers.js) +import { JsonRpcProvider } from 'ethers'; +const provider = new JsonRpcProvider(process.env.QUICKNODE_RPC_URL!); + +// EVM chains (viem) +import { createPublicClient, http } from 'viem'; +import { mainnet } from 'viem/chains'; +const client = createPublicClient({ + chain: mainnet, + transport: http(process.env.QUICKNODE_RPC_URL!), +}); + +// Solana +import { createSolanaRpc } from '@solana/kit'; +const rpc = createSolanaRpc(process.env.QUICKNODE_RPC_URL!); +``` + +### Authentication + +Quicknode endpoints include authentication in the URL: +``` +https://{ENDPOINT_NAME}.{NETWORK}.quiknode.pro/{API_KEY}/ +``` + +For additional security, enable JWT authentication or IP allowlisting in the Quicknode dashboard. + +### Supported Networks + +| Category | Networks | +|----------|----------| +| **EVM** | Ethereum, Polygon, Arbitrum, Optimism, Base, BSC, Avalanche, Fantom, zkSync, Scroll, Linea, Hyperliquid EVM (HyperEVM) | +| **Non-EVM** | Solana, Bitcoin, NEAR, Stacks, Cosmos, Sei, Aptos, Sui, TON, Hyperliquid (HyperCore) | + +Not exhaustive. Full list: https://www.quicknode.com/chains + +### Rate Limits & Plans + +As of 2026-02-02. Verify current limits in Quicknode docs before sizing a production system. + +| Plan | Requests/sec | Credits/month | +|------|-------------|---------------| +| Free Trial | 15 | 10M | +| Build | 50 | 80M | +| Accelerate | 125 | 450M | +| Scale | 250 | 950M | +| Business | 500 | 2B | + +See [references/rpc-reference.md](references/rpc-reference.md) for complete RPC documentation including method tables for EVM, Solana, and Bitcoin chains, WebSocket patterns, and batch request examples. + +## Streams + +Real-time & historical blockchain data pipelines that filter, transform, and deliver data to your destinations. + +### Stream Types + +| Type | Data | Use Case | +|------|------|----------| +| **Block** | Full block data | Block explorers, analytics | +| **Transaction** | Transaction details | Tx monitoring, indexing | +| **Logs** | Contract events | DeFi tracking, NFT sales, token transfers | +| **Receipt** | Transaction receipts | Gas analysis, status tracking | + +### Quick Setup + +1. Create stream in Quicknode dashboard +2. Select network and data type +3. Add filter function (JavaScript) +4. Configure destination (webhook, S3, database) + +### Basic Filter Function + +See [references/streams-reference.md](references/streams-reference.md) for filter examples and full Streams documentation. + +## Webhooks + +Event-driven notifications for blockchain activity. + +### Webhooks vs Streams + +| Feature | Webhooks | Streams | +|---------|----------|---------| +| **Setup** | Simple | More configuration | +| **Filtering** | Address/event-based | Custom JavaScript | +| **Destinations** | HTTP endpoint only | Webhook, S3, Postgres, Azure | +| **Processing** | Basic | Full transformation | +| **Use Case** | Simple alerts | Complex pipelines | + +### Webhook Setup + +See [references/webhooks-reference.md](references/webhooks-reference.md) for API examples and full Webhooks documentation. + +## IPFS Storage + +Decentralized file storage with Quicknode's IPFS gateway. + +See [references/ipfs-reference.md](references/ipfs-reference.md) for upload examples, metadata examples, and complete IPFS documentation. + + +## Marketplace Add-ons + +Enhanced APIs available through Quicknode's marketplace. + +### Token API (Ethereum) + +```javascript +// Get all token balances for an address +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_getWalletTokenBalance', + params: [{ wallet: '0x...', contracts: [] }] + }) +}); +``` + +### NFT API (Ethereum) + +```javascript +// Fetch NFTs owned by address +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_fetchNFTs', + params: [{ wallet: '0x...', page: 1, perPage: 10 }] + }) +}); +``` + +### Solana Priority Fee API + +```javascript +// Get recommended priority fees +const response = await rpc.request('qn_estimatePriorityFees', { + last_n_blocks: 100, + account: 'YOUR_ACCOUNT' +}).send(); +``` + +### Metis - Jupiter Swap API + +```typescript +// Using Metis - Jupiter Swap API +import { createJupiterApiClient } from '@jup-ag/api'; + +const jupiterApi = createJupiterApiClient({ + basePath: `${process.env.QUICKNODE_METIS_URL}` +}); + +const quote = await jupiterApi.quoteGet({ + inputMint: 'So11111111111111111111111111111111111111112', + outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + amount: 1000000000, + slippageBps: 50 +}); + +const swapResult = await jupiterApi.swapPost({ + swapRequest: { + quoteResponse: quote, + userPublicKey: 'YourPubkey...' + } +}); +``` + +See [references/marketplace-addons.md](references/marketplace-addons.md) for complete Add-ons documentation. + +## Solana DAS API (Digital Asset Standard) + +Comprehensive API for querying Solana digital assets — standard NFTs, compressed NFTs (cNFTs), fungible tokens, MPL Core Assets, and Token 2022 Assets. Available as a Marketplace add-on (Metaplex DAS API). + +**Docs:** https://www.quicknode.com/docs/solana/solana-das-api + +### Quick Setup + +```javascript +// Get all assets owned by a wallet +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByOwner', + params: { + ownerAddress: 'E645TckHQnDcavVv92Etc6xSWQaq8zzPtPRGBheviRAk', + limit: 10, + options: { showFungible: true, showCollectionMetadata: true } + } + }) +}); +const { result } = await response.json(); +// result.total — total assets +// result.items — array of asset metadata +// result.cursor — for pagination +``` + +### Available Methods + +| Method | Description | +|--------|-------------| +| `getAsset` | Get metadata for a single asset | +| `getAssets` | Get metadata for multiple assets | +| `getAssetProof` | Get Merkle proof for a compressed asset | +| `getAssetProofs` | Get Merkle proofs for multiple assets | +| `getAssetsByAuthority` | List assets by authority | +| `getAssetsByCreator` | List assets by creator | +| `getAssetsByGroup` | List assets by group (e.g., collection) | +| `getAssetsByOwner` | List assets by wallet owner | +| `getAssetSignatures` | Transaction signatures for compressed assets | +| `getTokenAccounts` | Token accounts by mint or owner | +| `getNftEditions` | Edition details of a master NFT | +| `searchAssets` | Search assets with flexible filters | + +See [references/solana-das-api-reference.md](references/solana-das-api-reference.md) for complete DAS API documentation with all methods, parameters, and examples. + +## Yellowstone gRPC (Solana) + +High-performance Solana Geyser plugin for real-time blockchain data streaming via gRPC. Available as a Marketplace add-on. + +### Quick Setup + +```typescript +import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; + +// Derive from HTTP URL: https://example.solana-mainnet.quiknode.pro/TOKEN/ +const client = new Client( + "https://example.solana-mainnet.quiknode.pro:10000", + "TOKEN", + {} +); + +const stream = await client.subscribe(); +stream.on("data", (data) => { + if (data.transaction) console.log("Tx:", data.transaction); +}); + +stream.write({ + transactions: { + txn_filter: { + vote: false, + failed: false, + accountInclude: ["PROGRAM_PUBKEY"], + accountExclude: [], + accountRequired: [], + }, + }, + accounts: {}, + slots: {}, + blocks: {}, + blocksMeta: {}, + transactionsStatus: {}, + entry: {}, + accountsDataSlice: [], + commitment: CommitmentLevel.CONFIRMED, +}); +``` + +### Filter Types + +| Filter | Description | +|--------|-------------| +| **accounts** | Account data changes by pubkey, owner, or data pattern | +| **transactions** | Transaction events with vote/failure/account filters | +| **transactionsStatus** | Lightweight transaction status updates | +| **slots** | Slot progression and status changes | +| **blocks** | Full block data with optional tx/account inclusion | +| **blocksMeta** | Block metadata without full contents | +| **entry** | PoH entry updates | + +See [references/yellowstone-grpc-reference.md](references/yellowstone-grpc-reference.md) for complete Yellowstone gRPC documentation. + +## HyperCore (Hyperliquid) + +Quicknode's data delivery infrastructure for the Hyperliquid L1 chain. Provides gRPC, JSON-RPC, WebSocket, and Info API access. Currently in public beta. + +### Access Methods + +| Method | Path / Port | Use Case | +|--------|-------------|----------| +| **Info API** | `/info` (POST) | 50+ methods for market data, positions, orders | +| **JSON-RPC** | `/hypercore` (POST) | Block queries (`hl_getBlock`, `hl_getBatchBlocks`) | +| **WebSocket** | `/hypercore/ws` | Real-time subscriptions (`hl_subscribe`) | +| **gRPC** | Port 10000 | Lowest-latency streaming for trades, orders, books | + +### gRPC Stream Types + +| Stream | Volume | Description | +|--------|--------|-------------| +| **TRADES** | High | Execution data: coin, price, size, side, fees | +| **ORDERS** | Very High | Order lifecycle with 18+ status types | +| **BOOK_UPDATES** | Very High | L2 order book diffs | +| **TWAP** | Low | Time-weighted average price order updates | +| **EVENTS** | High | Ledger updates, funding, deposits, withdrawals | +| **BLOCKS** | Extreme | Raw HyperCore blocks (gRPC only) | +| **WRITER_ACTIONS** | Low | System-level token transfers | + +### HyperEVM + +| Path | Debug/Trace | Archive | Use Case | +|------|-------------|---------|----------| +| `/evm` | No | Partial | Standard EVM operations | +| `/nanoreth` | Yes | Extended | Debug, trace, WebSocket subscriptions | + +See [references/hypercore-hyperliquid-reference.md](references/hypercore-hyperliquid-reference.md) for complete HyperCore and Hyperliquid documentation. + +## SQL Explorer + +Direct SQL access to indexed blockchain data without requiring infrastructure. Query billions of rows of on-chain data using standard SQL syntax and receive results in seconds. + +**Docs:** https://www.quicknode.com/docs/sql-explorer + +### Quick Setup + +```bash +curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \ + -H 'x-api-key: YOUR_API_KEY' \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "SELECT timestamp, coin, side, price, size FROM hyperliquid_trades WHERE block_time > now() - INTERVAL 1 HOUR ORDER BY block_number DESC LIMIT 10", + "clusterId": "hyperliquid-core-mainnet" + }' +``` + +### Coverage + +**Hyperliquid (HyperCore)** - Tables covering trades, orders, fills, funding, order book diffs, perpetual markets, spot markets, blocks, transactions, system actions, builder activity, staking, ledger updates, and more. + +### Key Features + +- **Infrastructure-free** - No database or indexer setup required +- **Standard SQL** - Full SQL syntax including joins, subqueries, CTEs, window functions +- **40+ pre-built queries** - Common patterns for trading analytics, whale tracking, liquidations +- **Optimized performance** - Monthly partitioning, sort keys, columnar storage +- **REST API** - Execute queries programmatically via HTTPS + +See [references/sql-explorer.md](references/sql-explorer.md) for complete table schemas, query examples, optimization tips, and API reference. + +## Quicknode SDK + +Official JavaScript/TypeScript SDK for Quicknode services. + +### Installation + +```bash +npm install @quicknode/sdk +``` + +### Basic Usage + +```typescript +import { Core } from '@quicknode/sdk'; + +const core = new Core({ + endpointUrl: process.env.QUICKNODE_RPC_URL!, +}); + +// Token API +const balances = await core.client.qn_getWalletTokenBalance({ + wallet: '0x...', +}); + +// NFT API +const nfts = await core.client.qn_fetchNFTs({ + wallet: '0x...', + page: 1, + perPage: 10, +}); +``` + +See [references/sdk-reference.md](references/sdk-reference.md) for complete SDK documentation. + +## Admin API + +REST API for programmatic management of Quicknode endpoints, usage, rate limits, security, billing, and teams. Enables infrastructure-as-code workflows. + +### Quick Reference + +| Resource | Methods | Endpoint | +|----------|---------|----------| +| Chains | GET | `/v0/chains` | +| Endpoints | GET, POST, PATCH, DELETE | `/v0/endpoints` | +| Metrics | GET | `/v0/endpoints/{id}/metrics` | +| Rate Limits | GET, POST, PUT | `/v0/endpoints/{id}/method-rate-limits`, `/v0/endpoints/{id}/rate-limits` | +| Security | GET | `/v0/endpoints/{id}/security_options` | +| Usage | GET | `/v0/usage/rpc`, `by-endpoint`, `by-method`, `by-chain` | +| Billing | GET | `/v0/billing/invoices` | +| Teams | GET | `/v0/teams` | + +### Authentication + +All requests use the `x-api-key` header against `https://api.quicknode.com/v0/`. + +```typescript +const QN_API_KEY = process.env.QUICKNODE_API_KEY!; + +const res = await fetch('https://api.quicknode.com/v0/endpoints', { + headers: { 'x-api-key': QN_API_KEY }, +}); +const endpoints = await res.json(); +``` + +See [references/admin-api-reference.md](references/admin-api-reference.md) for full Admin API documentation including endpoint CRUD, usage monitoring, rate limit configuration, security options, billing, and teams. + +## Key-Value Store (Beta) + +Serverless storage for lists and key-value sets, primarily accessed from within Streams filter functions via the `qnLib` helper library. Also available via REST API. + +### Stream Integration (qnLib) + +**List operations** — manage lists of items (e.g., wallet addresses): +- `qnLib.qnUpsertList` — create or update a list +- `qnLib.qnAddListItem` — add item to a list +- `qnLib.qnRemoveListItem` — remove item from a list +- `qnLib.qnContainsListItems` — batch membership check +- `qnLib.qnDeleteList` — delete a list + +**Set operations** — manage key-value pairs: +- `qnLib.qnAddSet` — create a key-value set +- `qnLib.qnGetSet` — retrieve value by key +- `qnLib.qnBulkSets` — bulk create/remove sets +- `qnLib.qnDeleteSet` — delete a set + +Docs: https://www.quicknode.com/docs/key-value-store + +## x402 (Pay-Per-Request RPC) + +Pay-per-request RPC access via stablecoin payments. No API key required. Two access patterns: pay-per-request ($0.001/call, no auth needed) and credit drawdown (SIWX auth, $10/1M requests). Supports USDC on Base/Polygon/Solana and USDG on XLayer. Access 140+ chain endpoints. + +### Quick Setup + +```typescript +import { wrapFetch } from "@x402/fetch"; +import { createWalletClient, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { base } from "viem/chains"; + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); +const walletClient = createWalletClient({ + account, + chain: base, + transport: http(), +}); + +// Wrap fetch to auto-handle 402 payments +const x402Fetch = wrapFetch(fetch, walletClient); + +// Use like normal fetch — payments are handled automatically +const response = await x402Fetch("https://x402.quicknode.com/ethereum-mainnet", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "eth_blockNumber", + params: [], + id: 1, + }), +}); +``` + +See [references/x402-reference.md](references/x402-reference.md) for complete x402 documentation including SIWE authentication, credit management, and the `@x402/fetch` wrapper. + +## MPP (Machine Payments Protocol) + +Pay-per-request RPC access via IETF Payment Authentication headers. No API key required. Two intent types: charge ($0.001/request) and session ($0.00001/request via off-chain vouchers). Payment via PathUSD or USDC.e on Tempo mainnet, PathUSD on Tempo testnet, or USDC on Solana. Access 140+ chain endpoints. + +### Quick Setup + +```typescript +import { Mppx, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +// Polyfills globalThis.fetch — handles 402 challenges automatically +Mppx.create({ + methods: [tempo({ account })], +}) + +// Charge intent ($0.001/req) — payment is transparent +const response = await fetch('https://mpp.quicknode.com/tempo-mainnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_blockNumber', + params: [], + }), +}) + +const { result } = await response.json() +console.log('Block number:', BigInt(result)) +``` + +See [references/mpp-reference.md](references/mpp-reference.md) for complete MPP documentation including charge vs session intents, Solana setup, CLI usage, and payment receipts. + +## Agent Subscriptions + +Programmatic Quicknode account creation for autonomous agents. A single POST to `/api/v1/agent/subscriptions` with an x402 or MPP payment creates a paid account synchronously and returns a `QN_*` full platform API key, no dashboard signup or email confirmation required. The same payment SDKs used for x402 and MPP per-request RPC sign the subscription payment. + +**Docs:** https://www.quicknode.com/docs/build-with-ai/agent-subscriptions + +### When to Use + +- Pay-per-request (x402, MPP) when the agent only needs short-lived RPC access with no persistent state. +- Agent Subscriptions when the agent needs the full platform: Streams, Webhooks, Key-Value Store, multiple endpoints, security rules, billing, or any other Admin API surface. + +### Discover Plans + +To fetch the live plan list, prices, accepted payment networks, asset contract addresses, and recipient (`payTo`) addresses, send the request without a payment header. The server returns HTTP 402 with the details in the body and a `PAYMENT-REQUIRED` header (base64-encoded x402 requirement). + +```bash +curl -X POST https://www.quicknode.com/api/v1/agent/subscriptions \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +Parse the 402 body, pick a plan, then retry the request through the SDK. + +### Quick Setup (x402) + +```typescript +import { createQuicknodeX402Client } from '@quicknode/x402' + +const client = await createQuicknodeX402Client({ + baseUrl: 'https://www.quicknode.com', + network: 'eip155:8453', // Base Mainnet + evmPrivateKey: process.env.PRIVATE_KEY as `0x${string}`, +}) + +const res = await client.fetch( + 'https://www.quicknode.com/api/v1/agent/subscriptions', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + plan_name: 'b6_build', // b6_build | b6_accelerate | b6_scale | b6_business + interval: 'monthly', // monthly | yearly + email: 'agent@example.com', + password: process.env.ACCOUNT_PASSWORD, + password_confirmation: process.env.ACCOUNT_PASSWORD, + full_name: 'Autonomous Agent', + name: 'Agent Account', + billing_address: { + line1: '123 Main St', + city: 'New York', + postal_code: '10001', + country: 'US', + }, + }), + }, +) + +const { api_key } = await res.json() // "QN_..." full platform API key +``` + +The returned `api_key` is the same `QUICKNODE_API_KEY` used everywhere else in this skill. Use it against `https://api.quicknode.com/v0/...` to provision endpoints, configure security, top up credits, read balances, and so on. + +> **Paying with Solana?** Use [`@quicknode/x402-solana`](https://github.com/quiknode-labs/x402-solana) to sign x402 payments with a Solana keypair instead of an EVM private key. Once configured, the subscription request body and Admin API steps are identical to the x402 example above. See the [How to Access Solana RPC with x402](https://www.quicknode.com/guides/solana-development/ai-agents/how-to-access-solana-rpc-with-x402-solana) guide for setup details. + +### Quick Setup (MPP) + +```typescript +import { Mppx, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +// Activate MPP. Polyfills globalThis.fetch so 402 challenges are +// intercepted, signed, and retried automatically inside fetch. +Mppx.create({ methods: [tempo({ account })] }) + +const subscriptionRes = await fetch( + 'https://www.quicknode.com/api/v1/agent/subscriptions', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + plan_name: 'b6_build', // b6_build | b6_accelerate | b6_scale | b6_business + interval: 'monthly', // monthly | yearly + email: 'agent@example.com', + password: process.env.ACCOUNT_PASSWORD, + password_confirmation: process.env.ACCOUNT_PASSWORD, + full_name: 'Autonomous Agent', + name: 'Agent Account', + billing_address: { + line1: '123 Main St', + city: 'New York', + postal_code: '10001', + country: 'US', + }, + }), + }, +) + +const { api_key } = await subscriptionRes.json() // "QN_..." full platform API key +``` + +### Endpoints + +| Method | Path | Auth | Rate limit | Purpose | +|--------|------|------|------------|---------| +| POST | `/api/v1/agent/subscriptions` | Payment header (x402 or MPP) | 30/min | Create account and subscription | +| POST | `/api/v1/agent/top_up` | API key + payment header | 30/min | Add credits to an existing subscription | +| GET | `/api/v1/agent/balance` | API key | 60/min | Read current credit balance | + +All requests target `https://www.quicknode.com`. + +### Plan IDs + +`b6_build`, `b6_accelerate`, `b6_scale`, `b6_business`. Map directly to the public Build / Accelerate / Scale / Business plans on [pricing](https://www.quicknode.com/pricing). Use the discovery 402 above when the agent needs to choose a plan dynamically. + +### Guard Rails + +- **Real funds**: Subscriptions settle in real stablecoins on a mainnet payment network. Confirm plan, interval, and payment network with the user before sending the request. +- **Duplicate email protection**: Re-using an existing email returns an error. Resume failed requests with the same `email` and `password` instead of creating a duplicate account. +- **No free trials**: All subscriptions are production-grade from the first request. +- **Synchronous creation**: Account creation and subscription activation happen in the same request. There is no async job or webhook to wait for. +- **No invented details**: Never fabricate billing details, passwords, or email addresses. Require explicit user input. Password must be 8–64 characters with at least one lowercase letter, one uppercase letter, one number, and one special character. + +## Common Patterns + +### Multi-Chain dApp Setup + +```typescript +import { Core } from '@quicknode/sdk'; + +const chains = { + ethereum: new Core({ + endpointUrl: 'https://YOUR_ETH_ENDPOINT.quiknode.pro/KEY/' + }), + polygon: new Core({ + endpointUrl: 'https://YOUR_POLYGON_ENDPOINT.quiknode.pro/KEY/' + }), + arbitrum: new Core({ + endpointUrl: 'https://YOUR_ARB_ENDPOINT.quiknode.pro/KEY/' + }), +}; + +// Use appropriate chain +const ethBalance = await chains.ethereum.client.getBalance({ address: '0x...' }); +``` + +### Real-Time Transaction Monitoring + +1. **Create Stream** filtering for your contract address +2. **Add Filter Function** to extract relevant events +3. **Configure Webhook** destination to your server +4. **Process Events** in your backend + +### NFT Collection Indexing + +1. **Use Streams** to capture mint/transfer events +2. **Store in Database** via PostgreSQL destination +3. **Query via Add-ons** for current ownership data +4. **Query via SDK** for custom API endpoints + +### Real-Time Solana Monitoring with Yellowstone gRPC + +1. **Connect via gRPC** on port 10000 with your auth token +2. **Subscribe to transactions** filtering by program or account +3. **Process updates** in real-time via the streaming interface +4. **Implement reconnection** with exponential backoff + +### Hyperliquid Trading Data Pipeline + +1. **Connect via gRPC** on port 10000 for lowest-latency data +2. **Subscribe to TRADES/ORDERS** streams with coin filters +3. **Process events** — handle ~12 blocks/sec throughput +4. **Use Info API** (`/info`) for account state and market metadata + +### Historical Trading Analysis with SQL Explorer + +1. **Query historical trades** — `SELECT * FROM hyperliquid_trades WHERE block_time > ...` +2. **Aggregate metrics** — Use `GROUP BY` for volume, counts, averages +3. **Join tables** — Combine trades, orders, funding for comprehensive analysis +4. **Export data** — Results as JSON for downstream processing + +## Best Practices + +### RPC +- Use WebSocket for subscriptions and real-time data +- Implement retry logic with exponential backoff +- Cache responses when data doesn't change frequently +- Use batch requests to reduce API calls + +### Streams +- Start with narrow filters, expand as needed +- Test filter functions locally before deployment +- Streams will automatically retry on failures +- Monitor stream health via dashboard + +### Security +- Store API keys in environment variables +- Enable IP allowlisting for production +- Use JWT authentication for sensitive operations +- Rotate API keys periodically + +### gRPC +- Enable zstd compression to reduce bandwidth (up to 70% for Hyperliquid) +- Implement reconnection logic with exponential backoff — streams can drop +- Use narrow filters (specific accounts, coins, or programs) to minimize data volume +- Set appropriate commitment levels (Yellowstone: CONFIRMED for most use cases, FINALIZED for irreversibility) +- Send keepalive pings (every 10s for Yellowstone, every 30s for Hypercore) to maintain connections + +### SQL Explorer +- Always filter by time ranges for partition pruning (`WHERE block_time > ...`) +- Use LIMIT on exploratory queries to reduce data scanned +- Leverage sort keys (check schema reference) for optimal query performance +- Start with pre-built queries and customize for your needs +- Monitor query statistics (rows_read, bytes_read) to optimize performance + +## Documentation Links + +### Quicknode Products +- **Main Docs**: https://www.quicknode.com/docs/ +- **Streams**: https://www.quicknode.com/docs/streams +- **Webhooks**: https://www.quicknode.com/docs/webhooks +- **IPFS**: https://www.quicknode.com/docs/ipfs +- **SDK**: https://www.quicknode.com/docs/quicknode-sdk +- **Admin API**: https://www.quicknode.com/docs/admin-api +- **DAS API (Solana)**: https://www.quicknode.com/docs/solana/solana-das-api +- **Yellowstone gRPC**: https://www.quicknode.com/docs/solana/yellowstone-grpc/overview +- **Hyperliquid**: https://www.quicknode.com/docs/hyperliquid +- **Hyperliquid gRPC**: https://www.quicknode.com/docs/hyperliquid/grpc-api +- **SQL Explorer**: https://www.quicknode.com/docs/sql-explorer +- **SQL Explorer REST API**: https://www.quicknode.com/docs/sql-explorer/using-rest-api +- **Hyperliquid Queries**: - https://www.quicknode.com/docs/sql-explorer/hyperliquid-queries +- **Schema Reference**: https://www.quicknode.com/docs/sql-explorer/schema-reference + +- **Key-Value Store**: https://www.quicknode.com/docs/key-value-store +- **x402**: https://x402.quicknode.com +- **MPP**: https://mpp.quicknode.com +- **Agent Subscriptions**: https://www.quicknode.com/docs/build-with-ai/agent-subscriptions +- **Build with AI Overview**: https://www.quicknode.com/docs/build-with-ai +- **Agents reference (agents.md)**: https://www.quicknode.com/agents.md + +### Chain-Specific Docs +- **Ethereum**: https://www.quicknode.com/docs/ethereum +- **Solana**: https://www.quicknode.com/docs/solana +- **Polygon**: https://www.quicknode.com/docs/polygon +- **Arbitrum**: https://www.quicknode.com/docs/arbitrum +- **Base**: https://www.quicknode.com/docs/base +- **Optimism**: https://www.quicknode.com/docs/optimism +- **Avalanche**: https://www.quicknode.com/docs/avalanche +- **BNB Smart Chain**: https://www.quicknode.com/docs/bnb-smart-chain +- **Hyperliquid**: https://www.quicknode.com/docs/hyperliquid + +### LLM-Optimized Documentation +- **Platform Overview (llms.txt)**: https://www.quicknode.com/llms.txt — High-level index of all Quicknode products, chains, guides, and solutions +- **Docs Index (llms.txt)**: https://www.quicknode.com/docs/llms.txt — Per-chain and per-product documentation index (links to `https://www.quicknode.com/docs/{chain-or-product}/llms.txt`) +- **x402 (llms.txt)**: https://x402.quicknode.com/llms.txt +- **MPP (llms.txt)**: https://mpp.quicknode.com/llms.txt + +### Additional Resources +- **Quicknode Guides**: https://www.quicknode.com/guides +- **SDK Reference**: https://www.quicknode.com/docs/quicknode-sdk +- **Marketplace**: https://marketplace.quicknode.com/ +- **Sample App Library**: https://www.quicknode.com/sample-app-library +- **Guide Examples Repo**: https://github.com/quiknode-labs/qn-guide-examples diff --git a/plugins/quicknode/skills/quicknode-skill/SKILL_SOURCE.md b/plugins/quicknode/skills/quicknode-skill/SKILL_SOURCE.md new file mode 100644 index 0000000..3f491b9 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/SKILL_SOURCE.md @@ -0,0 +1,5 @@ + +- Upstream repo: https://github.com/quiknode-labs/blockchain-skills +- Upstream path: skills/quicknode-skill +- Upstream commit: 40f2637251d3 +- Synced at: 2026-06-30T15:55:51Z diff --git a/plugins/quicknode/skills/quicknode-skill/references/admin-api-reference.md b/plugins/quicknode/skills/quicknode-skill/references/admin-api-reference.md new file mode 100644 index 0000000..7ed0b5b --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/admin-api-reference.md @@ -0,0 +1,415 @@ +# Quicknode Admin API Reference + +REST API for programmatic management of Quicknode endpoints, usage monitoring, rate limits, security, billing, and teams. + +## Quick Reference + +| Resource | Methods | Endpoints | +|----------|---------|-----------| +| **Chains** | GET | `/v0/chains` | +| **Endpoints** | GET, POST, PATCH, DELETE | `/v0/endpoints`, `/v0/endpoints/{id}` | +| **Endpoint Status** | PATCH | `/v0/endpoints/{id}/status` | +| **Endpoint Tags** | GET, POST | `/v0/endpoints/{id}/tags` | +| **Metrics** | GET | `/v0/endpoints/{id}/metrics` | +| **Rate Limits** | GET, POST, PUT | `/v0/endpoints/{id}/method-rate-limits`, `/v0/endpoints/{id}/rate-limits` | +| **Security** | GET | `/v0/endpoints/{id}/security_options` | +| **Usage** | GET | `/v0/usage/rpc`, `/v0/usage/rpc/by-endpoint`, `/v0/usage/rpc/by-method`, `/v0/usage/rpc/by-chain` | +| **Billing** | GET | `/v0/billing/invoices` | +| **Teams** | GET | `/v0/teams` | +| **Logs** | GET | `/v0/endpoints/{id}/logs` (Enterprise) | +| **Prometheus** | GET | `/exporter/prometheus` (Enterprise) | + +## Authentication + +Base URL: `https://api.quicknode.com/v0` + +All requests require the `x-api-key` header. Generate an API key from the Quicknode dashboard under **Settings > API Keys**. + +```typescript +const QN_API_KEY = process.env.QUICKNODE_API_KEY!; +const BASE_URL = 'https://api.quicknode.com/v0'; + +async function adminApi( + path: string, + options: RequestInit = {} +): Promise { + const res = await fetch(`${BASE_URL}${path}`, { + ...options, + headers: { + 'x-api-key': QN_API_KEY, + 'Content-Type': 'application/json', + ...options.headers, + }, + }); + if (!res.ok) { + const body = await res.text(); + throw new Error(`Admin API ${res.status}: ${body}`); + } + return res.json(); +} +``` + +All examples below reuse this `adminApi` helper. + +## Chains + +List all supported blockchain networks. + +```typescript +const chains = await adminApi<{ data: Array<{ + id: string; + name: string; + network: string; + chain: string; +}> }>('/chains'); + +chains.data.forEach(c => console.log(`${c.name} (${c.network})`)); +``` + +## Endpoints + +### List Endpoints + +```typescript +const endpoints = await adminApi<{ data: Array<{ + id: string; + name: string; + chain: string; + network: string; + http_url: string; + wss_url: string; + status: string; +}> }>('/endpoints'); + +endpoints.data.forEach(ep => + console.log(`${ep.name}: ${ep.chain}/${ep.network} [${ep.status}]`) +); +``` + +**Query Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `page` | number | No | Page number for pagination | +| `per_page` | number | No | Results per page (default: 25) | +| `tag` | string | No | Filter by tag name | + +```typescript +// Paginate with tag filter +const filtered = await adminApi('/endpoints?tag=production&page=1&per_page=10'); +``` + +### Create Endpoint + +```typescript +const newEndpoint = await adminApi('/endpoints', { + method: 'POST', + body: JSON.stringify({ + name: 'my-eth-mainnet', + chain: 'ethereum', + network: 'mainnet', + }), +}); + +console.log('Created:', newEndpoint.http_url); +``` + +**Body Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | Display name for the endpoint | +| `chain` | string | Yes | Blockchain (e.g., `ethereum`, `solana`) | +| `network` | string | Yes | Network (e.g., `mainnet`, `sepolia`, `devnet`) | + +### Get Endpoint + +```typescript +const endpoint = await adminApi(`/endpoints/${endpointId}`); +console.log(endpoint.http_url, endpoint.status); +``` + +### Update Endpoint + +```typescript +const updated = await adminApi(`/endpoints/${endpointId}`, { + method: 'PATCH', + body: JSON.stringify({ name: 'renamed-endpoint' }), +}); +``` + +### Pause / Unpause Endpoint + +```typescript +// Pause +await adminApi(`/endpoints/${endpointId}/status`, { + method: 'PATCH', + body: JSON.stringify({ status: 'paused' }), +}); + +// Unpause +await adminApi(`/endpoints/${endpointId}/status`, { + method: 'PATCH', + body: JSON.stringify({ status: 'active' }), +}); +``` + +### Archive (Delete) Endpoint + +```typescript +await adminApi(`/endpoints/${endpointId}`, { method: 'DELETE' }); +``` + +### Tags + +```typescript +// List tags on an endpoint +const tags = await adminApi(`/endpoints/${endpointId}/tags`); + +// Add tags +await adminApi(`/endpoints/${endpointId}/tags`, { + method: 'POST', + body: JSON.stringify({ tags: ['production', 'critical'] }), +}); +``` + +## Endpoint Metrics + +Retrieve performance metrics for an endpoint. + +```typescript +const metrics = await adminApi<{ data: { + total_requests: number; + success_rate: number; + avg_latency_ms: number; + p99_latency_ms: number; +} }>(`/endpoints/${endpointId}/metrics?period=24h&metric=requests`); + +console.log('Requests (24h):', metrics.data.total_requests); +console.log('Success rate:', metrics.data.success_rate); +console.log('Avg latency:', metrics.data.avg_latency_ms, 'ms'); +``` + +**Query Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `period` | string | No | Time period (`1h`, `24h`, `7d`, `30d`) | +| `metric` | string | No | Metric type (`requests`, `latency`, `errors`) | + +## Rate Limits + +### Get Method Rate Limits + +```typescript +const methodLimits = await adminApi( + `/endpoints/${endpointId}/method-rate-limits` +); +console.log(methodLimits); +``` + +### Set Method Rate Limits + +Restrict specific RPC methods on an endpoint. + +```typescript +await adminApi(`/endpoints/${endpointId}/method-rate-limits`, { + method: 'POST', + body: JSON.stringify({ + method_rate_limits: [ + { method: 'eth_getLogs', rate_limit: 50 }, + { method: 'debug_traceTransaction', rate_limit: 10 }, + ], + }), +}); +``` + +**Body Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `method_rate_limits` | array | Yes | Array of `{ method, rate_limit }` objects | +| `method_rate_limits[].method` | string | Yes | RPC method name | +| `method_rate_limits[].rate_limit` | number | Yes | Max requests per second for this method | + +### Set Global Rate Limits + +```typescript +await adminApi(`/endpoints/${endpointId}/rate-limits`, { + method: 'PUT', + body: JSON.stringify({ + rps: 100, // requests per second + rpm: 5000, // requests per minute + rpd: 500000 // requests per day + }), +}); +``` + +**Body Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `rps` | number | No | Requests per second | +| `rpm` | number | No | Requests per minute | +| `rpd` | number | No | Requests per day | + +## Security Options + +Retrieve security configuration for an endpoint. + +```typescript +const security = await adminApi( + `/endpoints/${endpointId}/security_options` +); +console.log(security); +``` + +**Security Option Types:** + +| Option | Description | +|--------|-------------| +| `jwt_authentication` | JSON Web Token auth for endpoint access | +| `ip_allowlist` | Restrict access to specific IP addresses | +| `referrer_allowlist` | Restrict access to specific HTTP referrers | +| `token_authentication` | Additional token-based authentication | + +## Usage + +### Total RPC Usage + +```typescript +const usage = await adminApi<{ data: { + total_requests: number; + total_credits: number; +} }>('/usage/rpc?start_time=2025-01-01T00:00:00Z&end_time=2025-01-31T23:59:59Z'); + +console.log('Total requests:', usage.data.total_requests); +console.log('Credits used:', usage.data.total_credits); +``` + +**Query Parameters (shared across all usage endpoints):** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `start_time` | string | Yes | ISO 8601 start time | +| `end_time` | string | Yes | ISO 8601 end time | + +### Usage by Endpoint + +```typescript +const byEndpoint = await adminApi( + '/usage/rpc/by-endpoint?start_time=2025-01-01T00:00:00Z&end_time=2025-01-31T23:59:59Z' +); +``` + +### Usage by Method + +```typescript +const byMethod = await adminApi( + '/usage/rpc/by-method?start_time=2025-01-01T00:00:00Z&end_time=2025-01-31T23:59:59Z' +); +``` + +### Usage by Chain + +```typescript +const byChain = await adminApi( + '/usage/rpc/by-chain?start_time=2025-01-01T00:00:00Z&end_time=2025-01-31T23:59:59Z' +); +``` + +## Billing + +Retrieve invoice history. + +```typescript +const invoices = await adminApi<{ data: Array<{ + id: string; + amount: number; + currency: string; + status: string; + period_start: string; + period_end: string; +}> }>('/billing/invoices'); + +invoices.data.forEach(inv => + console.log(`${inv.period_start} - ${inv.period_end}: $${inv.amount / 100} [${inv.status}]`) +); +``` + +## Teams + +List team members and roles. + +```typescript +const teams = await adminApi<{ data: Array<{ + id: string; + name: string; + members: Array<{ + email: string; + role: string; + status: string; + }>; +}> }>('/teams'); + +teams.data.forEach(team => { + console.log(`Team: ${team.name}`); + team.members.forEach(m => console.log(` ${m.email} (${m.role})`)); +}); +``` + +## Logs (Enterprise) + +Query request logs for an endpoint. Available on Enterprise plans. + +```typescript +const logs = await adminApi<{ data: Array<{ + timestamp: string; + method: string; + status_code: number; + latency_ms: number; + ip_address: string; +}> }>(`/endpoints/${endpointId}/logs?from=2025-01-01T00:00:00Z&to=2025-01-02T00:00:00Z&limit=100`); + +logs.data.forEach(log => + console.log(`${log.timestamp} ${log.method} ${log.status_code} ${log.latency_ms}ms`) +); +``` + +**Query Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `from` | string | Yes | ISO 8601 start time | +| `to` | string | Yes | ISO 8601 end time | +| `limit` | number | No | Max results (default: 100, max: 1000) | +| `next_at` | string | No | Cursor for pagination | + +## Prometheus (Enterprise) + +Export endpoint metrics in Prometheus format for integration with Grafana or other monitoring tools. + +``` +GET https://api.quicknode.com/exporter/prometheus +Header: x-api-key: YOUR_API_KEY +``` + +Returns metrics in Prometheus exposition format, suitable for scraping. + +## Best Practices + +1. **API key storage** — Store keys in environment variables or a secrets manager. Never hardcode in source. +2. **Pagination** — Use `page` and `per_page` on list endpoints to avoid large responses. +3. **Retry logic** — Implement exponential backoff for transient `5xx` errors. +4. **Caching** — Cache chain and endpoint list responses (they change infrequently). +5. **Tagging** — Tag endpoints by environment (`production`, `staging`, `dev`) for easy filtering. +6. **Usage monitoring** — Poll usage endpoints periodically and alert when approaching plan limits. +7. **Rate limit headroom** — Set method-level limits below your plan ceiling to prevent runaway consumers. + +## Documentation + +- **Admin API Docs**: https://www.quicknode.com/docs/admin-api +- **Admin API Docs (llms.txt)**: https://www.quicknode.com/docs/admin-api/llms.txt +- **Quicknode Dashboard**: https://dashboard.quicknode.com/ +- **API Key Management**: https://dashboard.quicknode.com/settings/api-keys +- **Guides**: https://www.quicknode.com/guides/tags/admin-api diff --git a/plugins/quicknode/skills/quicknode-skill/references/hypercore-hyperliquid-reference.md b/plugins/quicknode/skills/quicknode-skill/references/hypercore-hyperliquid-reference.md new file mode 100644 index 0000000..b45174a --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/hypercore-hyperliquid-reference.md @@ -0,0 +1,522 @@ +# HyperCore & Hyperliquid Reference + +Quicknode provides infrastructure for the Hyperliquid L1 chain through HyperCore, delivering gRPC, JSON-RPC, WebSocket, and Info API access to exchange and trading data, plus HyperEVM RPC for smart contract execution. + +## Overview + +| Property | Value | +|----------|-------| +| **Chain** | Hyperliquid L1 | +| **Consensus** | HyperBFT (based on HotStuff) | +| **Native Token** | HYPE | +| **Mainnet Chain ID** | 999 | +| **Testnet Chain ID** | 998 | +| **Block Rate** | ~12 blocks/sec | +| **Status** | Public Beta | +| **gRPC Compression** | zstd (~70% bandwidth reduction) | +| **Architecture** | HyperCore (exchange/trading) + HyperEVM (smart contracts) | + +## Network Configuration + +| Network | Endpoint Pattern | Chain ID | +|---------|-----------------|----------| +| **Mainnet** | `https://[name].hype-mainnet.quiknode.pro/[token]/` | 999 (0x3E7) | +| **Testnet** | `https://[name].hype-testnet.quiknode.pro/[token]/` | 998 | + +Testnet is pruned to the last 250 blocks. + +## HyperCore Access Methods + +| Method | Path / Port | Protocol | Description | +|--------|-------------|----------|-------------| +| **Info** | `/info` | HTTP POST | 50+ specialized methods for market data, positions, orders | +| **JSON-RPC** | `/hypercore` | HTTP POST | Block queries: `hl_getLatestBlocks`, `hl_getBlock`, `hl_getBatchBlocks` | +| **WebSocket** | `/hypercore/ws` | WebSocket | Real-time subscriptions: `hl_subscribe`, `hl_unsubscribe` | +| **gRPC** | Port 10000 | gRPC (HTTP/2) | Lowest latency streaming: `Ping`, `StreamBlocks`, `StreamData` | + +## Authentication + +### URL Token (default) + +``` +https://your-endpoint.hype-mainnet.quiknode.pro/your-auth-token/ +``` + +### Header-Based + +```bash +curl -H "x-token: your-auth-token" \ + https://your-endpoint.hype-mainnet.quiknode.pro/evm +``` + +### gRPC Authentication + +```javascript +const grpc = require("@grpc/grpc-js"); + +const metadata = new grpc.Metadata(); +metadata.add("x-token", "your-auth-token"); +// Pass metadata to all gRPC calls +``` + +Endpoint for gRPC: `your-endpoint.hype-mainnet.quiknode.pro:10000` (TLS required). + +## Info Endpoint + +The Info API provides 50+ methods for querying Hyperliquid exchange data. All requests are `POST` to `/info` with a `type` field. + +> **Note:** Some Info methods (e.g., `allMids`, `l2Book`, `meta`) are also available via Hyperliquid's public endpoints without a Quicknode subscription. Check https://www.quicknode.com/docs/hyperliquid/llms.txt for details on which methods require a Quicknode endpoint vs. public access. + +### Base URL + +``` +https://[endpoint].hype-mainnet.quiknode.pro/[token]/info +``` + +### Quick Example + +```typescript +const response = await fetch( + `${process.env.QUICKNODE_RPC_URL}info`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ type: "allMids" }), + } +); +const midPrices = await response.json(); +// { "BTC": "92385.0", "ETH": "3167.4", ... } +``` + +### Key Methods + +| Method (`type`) | Parameters | Description | +|-----------------|------------|-------------| +| `allMids` | — | Real-time mid-market prices for all pairs | +| `l2Book` | `coin` | Level 2 order book (up to 20 levels per side) | +| `recentTrades` | `coin` | Recent executed trades | +| `candleSnapshot` | `coin`, `interval`, `startTime`, `endTime` | OHLCV candlestick data | +| `meta` | — | Exchange metadata: trading pairs, leverage limits | +| `metaAndAssetCtxs` | — | Market data with funding, OI, oracle prices | +| `spotMeta` | — | Spot market metadata | +| `spotMetaAndAssetCtxs` | — | Spot metadata with prices | +| `clearinghouseState` | `user` | Account positions, margin, P&L | +| `spotClearinghouseState` | `user` | Spot token balances | +| `openOrders` | `user` | All open orders for a user | +| `frontendOpenOrders` | `user` | Open orders (frontend format) | +| `historicalOrders` | `user` | Up to 2,000 recent historical orders | +| `orderStatus` | `user`, `oid` | Status of a specific order | +| `userFills` | `user` | Up to 2,000 recent trade executions | +| `userFillsByTime` | `user`, `startTime` | Fills within a time range | +| `fundingHistory` | `coin`, `startTime` | Historical funding rates | +| `predictedFundings` | — | Forecasted funding rates | +| `activeAssetData` | `user`, `coin` | Active trading data for user/asset | +| `portfolio` | `user` | Account value and P&L history | +| `vaultDetails` | `vaultAddress` | Vault analytics | +| `exchangeStatus` | — | Exchange status and maintenance info | + +## JSON-RPC Methods + +POST requests to `/hypercore`. + +### hl_getLatestBlocks + +```typescript +const response = await fetch( + `${process.env.QUICKNODE_RPC_URL}hypercore`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "hl_getLatestBlocks", + params: { stream: "trades", count: 10 }, + id: 1, + }), + } +); +const { result } = await response.json(); +// result.blocks: [{ local_time, block_time, block_number, events }] +``` + +### hl_getBlock + +```typescript +const response = await fetch( + `${process.env.QUICKNODE_RPC_URL}hypercore`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "hl_getBlock", + params: ["trades", 817824084], + id: 1, + }), + } +); +``` + +### hl_getBatchBlocks + +```typescript +const response = await fetch( + `${process.env.QUICKNODE_RPC_URL}hypercore`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "hl_getBatchBlocks", + params: { stream: "trades", from: 817824078, to: 817824090 }, + id: 1, + }), + } +); +``` + +### hl_getLatestBlockNumber + +```typescript +const response = await fetch( + `${process.env.QUICKNODE_RPC_URL}hypercore`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "hl_getLatestBlockNumber", + params: ["events"], + id: 1, + }), + } +); +``` + +## WebSocket + +Connect to `/hypercore/ws` for real-time subscriptions. + +### Subscribe + +```typescript +import WebSocket from "ws"; + +const ws = new WebSocket( + `${process.env.QUICKNODE_WSS_URL}hypercore/ws` +); + +ws.on("open", () => { + // Subscribe to trades + ws.send( + JSON.stringify({ + jsonrpc: "2.0", + method: "hl_subscribe", + params: { streamType: "trades" }, + id: 1, + }) + ); +}); + +ws.on("message", (data) => { + const message = JSON.parse(data.toString()); + if (message.params) { + console.log("Trade event:", message.params); + } +}); + +// Unsubscribe +ws.send( + JSON.stringify({ + jsonrpc: "2.0", + method: "hl_unsubscribe", + params: { streamType: "trades" }, + id: 2, + }) +); +``` + +## gRPC Streaming + +Port 10000 provides the lowest-latency access to HyperCore data via three RPC methods. + +### Connection Setup + +```typescript +const grpc = require("@grpc/grpc-js"); +const protoLoader = require("@grpc/proto-loader"); + +const ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"; +const TOKEN = "your-auth-token"; + +const packageDefinition = protoLoader.loadSync("streaming.proto", { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, +}); + +const proto = grpc.loadPackageDefinition(packageDefinition); +const channelCredentials = grpc.credentials.createSsl(); +const client = new proto.streaming.Streaming(ENDPOINT, channelCredentials, { + "grpc.max_receive_message_length": 100 * 1024 * 1024, // 100MB +}); + +const metadata = new grpc.Metadata(); +metadata.add("x-token", TOKEN); +``` + +### gRPC Methods + +| Method | Type | Description | +|--------|------|-------------| +| `Ping` | Unary | Connection health check | +| `StreamBlocks` | Server streaming | Stream blocks from a timestamp | +| `StreamData` | Bidirectional streaming | Subscribe to filtered data streams | + +### Ping + +```typescript +client.Ping({ count: 1 }, metadata, (error, response) => { + if (error) console.error("Ping failed:", error); + else console.log("Ping response:", response); +}); +``` + +### StreamData (Bidirectional) + +```typescript +const stream = client.StreamData(metadata); + +// Subscribe to trades for specific coins +stream.write({ + subscribe: { + stream_type: "TRADES", + coins: ["BTC", "ETH"], + }, +}); + +// Send keepalive pings every 30 seconds +const pingInterval = setInterval(() => { + stream.write({ ping: { timestamp: Date.now() } }); +}, 30000); + +stream.on("data", (response) => { + if (response.data) { + const block = JSON.parse(response.data.data); + console.log("Block:", response.data.block_number, block); + } + if (response.pong) { + console.log("Pong:", response.pong.timestamp); + } +}); + +stream.on("error", (error) => { + console.error("Stream error:", error); + clearInterval(pingInterval); +}); + +stream.on("end", () => { + clearInterval(pingInterval); +}); +``` + +## gRPC Stream Types + +| Stream Type | Volume | Available Via | +|-------------|--------|---------------| +| **TRADES** | High | gRPC, JSON-RPC, WebSocket | +| **ORDERS** | Very High | gRPC, JSON-RPC, WebSocket | +| **BOOK_UPDATES** | Very High | gRPC, JSON-RPC, WebSocket | +| **TWAP** | Low | gRPC, JSON-RPC, WebSocket | +| **EVENTS** | High | gRPC, JSON-RPC, WebSocket | +| **BLOCKS** | Extreme | gRPC only | +| **WRITER_ACTIONS** | Low | gRPC, JSON-RPC, WebSocket | + +### Stream Data Details + +- **TRADES**: Execution data — coin, price, size, side, fees, liquidation info +- **ORDERS**: Order lifecycle — 18+ status types (open, filled, canceled, rejected variants) +- **BOOK_UPDATES**: Level-2 order book diffs — individual order adds/removes +- **TWAP**: Time-weighted average price order updates — activated, finished, terminated +- **EVENTS**: Ledger updates, funding payments, deposits, withdrawals, delegations +- **BLOCKS**: Raw HyperCore blocks with all 34 action types (gRPC only) +- **WRITER_ACTIONS**: System-level spot token transfers (HyperCore to HyperEVM) + +## gRPC Filtering + +Filter streams by coin, user, side, and other fields depending on stream type. + +### Filter Fields by Stream + +| Stream | Available Filters | +|--------|-------------------| +| **TRADES** | `coin`, `user`, `side`, `liquidation`, `builder` | +| **ORDERS** | `coin`, `user`, `status`, `builder` | +| **BOOK_UPDATES** | `coin`, `side` | +| **TWAP** | `coin`, `user`, `status` | +| **EVENTS** | `user`, `type` | +| **WRITER_ACTIONS** | `user`, `action.type`, `action.token` | + +### Filter Logic + +- **AND across fields** — When multiple filter fields are specified (e.g., `coin` and `side`), all conditions must match (AND logic). +- **OR within values** — When a field has multiple values (e.g., `coin: { values: ["BTC", "ETH"] }`), any value can match (OR logic). +- **Special value `"*"`** — Matches any event where the field exists (non-null). +- **Special value `"null"`** — Matches events where the field is explicitly null. +- **Recursive matching** — Filters match recursively into nested JSON structures, so top-level field filters also apply to nested objects. + +### Filter Limits + +| Limit | Maximum | +|-------|---------| +| Values per `user` / `address` filter | 100 | +| Values per `coin` filter | 50 | +| Values per `type` / `status` filter | 20 | +| Total filter values across all fields | 500 | +| Named filters per stream | 10 | + +### Filtering Examples + +```typescript +const stream = client.StreamData(metadata); + +// Subscribe to BTC and ETH buy trades only +stream.write({ + subscribe: { + stream_type: "TRADES", + filters: { + coin: { values: ["BTC", "ETH"] }, + side: { values: ["B"] }, + }, + }, +}); + +// Subscribe to order status changes for a specific user +stream.write({ + subscribe: { + stream_type: "ORDERS", + filters: { + user: { values: ["0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"] }, + status: { values: ["filled", "canceled"] }, + }, + }, +}); + +// Subscribe to all events where the user field exists +stream.write({ + subscribe: { + stream_type: "EVENTS", + filters: { + user: { values: ["*"] }, + }, + }, +}); + +// Subscribe to liquidation trades only +stream.write({ + subscribe: { + stream_type: "TRADES", + filters: { + liquidation: { values: ["*"] }, + }, + }, +}); +``` + +## HyperEVM + +HyperEVM provides EVM-compatible smart contract execution on Hyperliquid. Two RPC paths are available: + +| Path | Protocol | Archive | Debug/Trace | Use Case | +|------|----------|---------|-------------|----------| +| `/evm` | HTTP | Partial | No | Standard blockchain operations | +| `/nanoreth` | HTTP + WebSocket | Extended | Yes (`debug_*`, `trace_*`) | Advanced debugging, tracing, subscriptions | + +### Standard EVM Example (`/evm`) + +```typescript +import { JsonRpcProvider } from "ethers"; + +const provider = new JsonRpcProvider( + `${process.env.QUICKNODE_RPC_URL}evm` +); + +const blockNumber = await provider.getBlockNumber(); +const balance = await provider.getBalance("0x..."); +``` + +### Debug/Trace Example (`/nanoreth`) + +```typescript +import { JsonRpcProvider } from "ethers"; + +const provider = new JsonRpcProvider( + `${process.env.QUICKNODE_RPC_URL}nanoreth` +); + +// Standard methods work on nanoreth too +const blockNumber = await provider.getBlockNumber(); + +// Debug and trace methods only available on /nanoreth +const trace = await provider.send("debug_traceTransaction", [ + "0xTransactionHash...", + { tracer: "callTracer" }, +]); +``` + +### WebSocket Subscriptions (`/nanoreth`) + +```typescript +import { WebSocketProvider } from "ethers"; + +const wsProvider = new WebSocketProvider( + `${process.env.QUICKNODE_WSS_URL}nanoreth` +); + +wsProvider.on("block", (blockNumber) => { + console.log("New block:", blockNumber); +}); +``` + +### Hyperliquid-Specific EVM Methods + +| Method | Description | +|--------|-------------| +| `eth_getSystemTxsByBlockNumber` | Internal system transactions (HyperCore-to-HyperEVM) | +| `eth_getSystemTxsByBlockHash` | System transactions by block hash | +| `eth_usingBigBlocks` | Check if address uses big blocks | +| `eth_bigBlockGasPrice` | Gas price for big blocks | + +## Best Practices + +1. **Use gRPC for lowest latency** — Port 10000 gRPC streaming provides sub-millisecond data delivery, ideal for trading applications. +2. **Enable zstd compression** — Reduces bandwidth by ~70%, critical for high-volume streams like ORDERS and BOOK_UPDATES. +3. **Use `/nanoreth` for debugging** — Extended archive and trace/debug methods are only available on `/nanoreth`, not `/evm`. +4. **Handle ~12 blocks/sec throughput** — Hyperliquid produces blocks rapidly. Ensure your consumer can process events at this rate. +5. **Send gRPC keepalive pings** — Send pings every 30 seconds to maintain the connection. +6. **Note public beta status** — HyperCore on Quicknode is in public beta. APIs and behavior may change. + +## Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| gRPC connection refused on port 10000 | Wrong endpoint or port | Use `endpoint.hype-mainnet.quiknode.pro:10000` with TLS | +| Auth failed on gRPC | Missing or wrong `x-token` metadata | Add `metadata.add('x-token', TOKEN)` to all gRPC calls | +| No data from `/info` | Wrong path or missing `type` field | POST to `/info` with `{"type": "methodName"}` | +| WebSocket disconnects | No ping/pong or server maintenance | Implement reconnection logic with backoff | +| `/evm` missing debug methods | Debug methods not available on `/evm` | Switch to `/nanoreth` for `debug_*` and `trace_*` methods | +| Testnet data missing | Testnet pruned to last 250 blocks | Use mainnet for historical data; testnet is for testing only | +| High bandwidth usage | Unfiltered high-volume streams | Apply coin/user/side filters and enable zstd compression | + +## Documentation + +- **Hyperliquid Overview**: https://www.quicknode.com/docs/hyperliquid +- **Hyperliquid Overview (llms.txt)**: : https://www.quicknode.com/docs/hyperliquid/llms.txt +- **Hyperliquid gRPC API**: https://www.quicknode.com/docs/hyperliquid/grpc-api +- **HyperCore Filtering**: https://www.quicknode.com/docs/hyperliquid/filtering +- **Hyperliquid llms.txt**: https://www.quicknode.com/docs/hyperliquid/llms.txt +- **HyperCore Info Methods**: https://www.quicknode.com/docs/hyperliquid (Info endpoint section) +- **HyperEVM**: https://www.quicknode.com/docs/hyperliquid (HyperEVM section) +- **Guides**: https://www.quicknode.com/guides/tags/hyperliquid diff --git a/plugins/quicknode/skills/quicknode-skill/references/ipfs-reference.md b/plugins/quicknode/skills/quicknode-skill/references/ipfs-reference.md new file mode 100644 index 0000000..bf44a13 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/ipfs-reference.md @@ -0,0 +1,390 @@ +# Quicknode IPFS Reference + +Quicknode provides IPFS (InterPlanetary File System) storage for decentralized file hosting, ideal for NFT metadata, images, and other blockchain-related assets. + +## Overview + +| Feature | Description | +|---------|-------------| +| **Storage** | Persistent IPFS pinning | +| **Gateway** | Dedicated IPFS gateway for fast retrieval | +| **API** | REST API for uploads and management | +| **Formats** | Any file type (images, JSON, video, etc.) | + +## Upload Files + +### Single File Upload + +```javascript +const FormData = require('form-data'); +const fs = require('fs'); + +async function uploadFile(filePath) { + const form = new FormData(); + form.append('file', fs.createReadStream(filePath)); + + const response = await fetch('https://api.quicknode.com/ipfs/rest/v1/s3/put-object', { + method: 'POST', + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY + }, + body: form + }); + + const result = await response.json(); + console.log(`CID: ${result.pin.cid}`); + console.log(`Gateway URL: https://YOUR_GATEWAY.quicknode-ipfs.com/ipfs/${result.pin.cid}`); + + return result; +} + +await uploadFile('./image.png'); +``` + +### Upload JSON Data + +```javascript +async function uploadJSON(data) { + const response = await fetch('https://api.quicknode.com/ipfs/rest/v1/s3/put-object', { + method: 'POST', + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }); + + const result = await response.json(); + return result.pin.cid; +} + +const metadata = { + name: "My NFT", + description: "An awesome NFT", + image: "ipfs://QmImageCid..." +}; + +const cid = await uploadJSON(metadata); +``` + +### Upload from Buffer + +```javascript +async function uploadBuffer(buffer, filename) { + const form = new FormData(); + form.append('file', buffer, { filename }); + + const response = await fetch('https://api.quicknode.com/ipfs/rest/v1/s3/put-object', { + method: 'POST', + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY + }, + body: form + }); + + return await response.json(); +} +``` + +## Directory Upload + +Upload entire directories while preserving structure. + +```javascript +const FormData = require('form-data'); +const fs = require('fs'); +const path = require('path'); + +async function uploadDirectory(dirPath) { + const form = new FormData(); + const files = fs.readdirSync(dirPath); + + for (const file of files) { + const filePath = path.join(dirPath, file); + form.append('file', fs.createReadStream(filePath), { + filepath: file // Preserves filename in IPFS + }); + } + + const response = await fetch('https://api.quicknode.com/ipfs/rest/v1/s3/put-object', { + method: 'POST', + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY + }, + body: form + }); + + return await response.json(); +} + +// Upload NFT collection assets +await uploadDirectory('./nft-images/'); +``` + +## Pin by CID + +Pin existing IPFS content to Quicknode's infrastructure. + +```javascript +async function pinByCID(cid, name) { + const response = await fetch('https://api.quicknode.com/ipfs/rest/v1/pinning/pinByHash', { + method: 'POST', + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + cid: cid, + name: name || `Pinned: ${cid}` + }) + }); + + return await response.json(); +} + +// Pin existing content +await pinByCID('QmExistingCID...', 'My Pinned Content'); +``` + +## Retrieve Content + +### Via Gateway + +```javascript +const GATEWAY_URL = 'https://YOUR_GATEWAY.quicknode-ipfs.com'; + +// Fetch JSON metadata +async function fetchMetadata(cid) { + const response = await fetch(`${GATEWAY_URL}/ipfs/${cid}`); + return await response.json(); +} + +// Fetch image as buffer +async function fetchImage(cid) { + const response = await fetch(`${GATEWAY_URL}/ipfs/${cid}`); + return await response.buffer(); +} +``` + +### Gateway URL Formats + +``` +# Direct CID access +https://YOUR_GATEWAY.quicknode-ipfs.com/ipfs/{cid} + +# Path within directory +https://YOUR_GATEWAY.quicknode-ipfs.com/ipfs/{directoryCid}/image.png + +# IPNS resolution +https://YOUR_GATEWAY.quicknode-ipfs.com/ipns/{name} +``` + +## List Pinned Content + +```javascript +async function listPins(pageNumber = 1, perPage = 100) { + const response = await fetch( + `https://api.quicknode.com/ipfs/rest/v1/pinning/pins?pageNumber=${pageNumber}&perPage=${perPage}`, + { + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY + } + } + ); + + return await response.json(); +} + +const pins = await listPins(); +console.log(`Total pinned: ${pins.totalItems}`); +pins.data.forEach(pin => { + console.log(`${pin.cid} - ${pin.name}`); +}); +``` + +## Unpin Content + +```javascript +async function unpin(requestId) { + const response = await fetch( + `https://api.quicknode.com/ipfs/rest/v1/pinning/pins/${requestId}`, + { + method: 'DELETE', + headers: { + 'x-api-key': process.env.QUICKNODE_API_KEY + } + } + ); + + return response.status === 200; +} + +// Use the requestId from the pin response, not the CID +await unpin('12345678-abcd-...'); +``` + +## NFT Metadata Examples + +### Standard NFT Metadata + +```javascript +const nftMetadata = { + name: "Cool Cat #1234", + description: "A very cool cat from the Cool Cats collection", + image: "ipfs://QmImageCID...", + external_url: "https://coolcats.com/cat/1234", + attributes: [ + { + trait_type: "Background", + value: "Blue" + }, + { + trait_type: "Fur", + value: "Orange" + }, + { + trait_type: "Eyes", + value: "Laser" + }, + { + trait_type: "Rarity Score", + value: 85, + display_type: "number" + } + ] +}; + +const metadataCID = await uploadJSON(nftMetadata); +console.log(`Token URI: ipfs://${metadataCID}`); +``` + +### Collection-Level Metadata + +```javascript +const collectionMetadata = { + name: "Cool Cats", + description: "10,000 randomly generated Cool Cats", + image: "ipfs://QmCollectionImageCID...", + external_link: "https://coolcats.com", + seller_fee_basis_points: 500, // 5% + fee_recipient: "0xYourAddress..." +}; + +await uploadJSON(collectionMetadata); +``` + +### Batch Upload NFT Collection + +```javascript +async function uploadNFTCollection(basePath, count) { + const results = []; + + for (let i = 1; i <= count; i++) { + // Upload image + const imagePath = `${basePath}/images/${i}.png`; + const imageResult = await uploadFile(imagePath); + const imageCID = imageResult.pin.cid; + + // Create and upload metadata + const metadata = { + name: `My NFT #${i}`, + description: `NFT number ${i} from my collection`, + image: `ipfs://${imageCID}`, + attributes: [ + { trait_type: "Number", value: i } + ] + }; + + const metadataCID = await uploadJSON(metadata); + + results.push({ + tokenId: i, + imageCID, + metadataCID, + tokenURI: `ipfs://${metadataCID}` + }); + + console.log(`Uploaded NFT #${i}`); + } + + return results; +} +``` + +## Dedicated Gateway Configuration + +### Custom Domain + +1. Navigate to Quicknode Dashboard → IPFS → Gateways +2. Create dedicated gateway +3. Configure custom domain (optional) +4. Set up DNS CNAME record + +### Gateway Features + +| Feature | Description | +|---------|-------------| +| **Caching** | Edge caching for fast delivery | +| **Rate Limits** | Configurable per gateway | +| **Analytics** | Request metrics and bandwidth | +| **Access Control** | IP allowlisting, authentication | + +## Storage Plans + +> **Note:** Storage plans and limits may change. Verify current pricing and limits at https://www.quicknode.com/pricing or in the QuickNode dashboard. + +| Plan | Storage | Bandwidth | Pins | +|------|---------|-----------|------| +| Free | 1 GB | 5 GB/mo | 500 | +| Starter | 10 GB | 50 GB/mo | 5,000 | +| Growth | 100 GB | 500 GB/mo | 50,000 | +| Business | Custom | Custom | Custom | + +## Best Practices + +1. **Use CIDs in contracts** - Store `ipfs://CID` format in smart contracts +2. **Upload images first** - Get image CID before creating metadata +3. **Verify uploads** - Always verify content is accessible via gateway +4. **Batch operations** - Upload multiple files concurrently when possible +5. **Pin important content** - Ensure critical content is pinned to multiple services +6. **Use directory uploads** - For collections, upload as directory for cleaner structure + +## Error Handling + +```javascript +async function safeUpload(filePath) { + try { + const result = await uploadFile(filePath); + + // Verify upload + const verifyResponse = await fetch( + `https://YOUR_GATEWAY.quicknode-ipfs.com/ipfs/${result.pin.cid}`, + { method: 'HEAD' } + ); + + if (!verifyResponse.ok) { + throw new Error('Upload verification failed'); + } + + return result; + } catch (error) { + console.error('Upload failed:', error.message); + throw error; + } +} +``` + +## Common Issues + +| Issue | Cause | Solution | +|-------|-------|----------| +| Slow uploads | Large files | Compress before upload | +| 404 on gateway | Not yet propagated | Wait or use direct pin | +| Rate limited | Too many requests | Implement backoff | +| Content not found | Unpinned | Re-pin content | + +## Documentation + +- **IPFS Overview**: https://www.quicknode.com/docs/ipfs +- **IPFS Overview (llms.txt)**: https://www.quicknode.com/docs/ipfs/llms.txt +- **Guides**: https://www.quicknode.com/guides/tags/ipfs diff --git a/plugins/quicknode/skills/quicknode-skill/references/marketplace-addons.md b/plugins/quicknode/skills/quicknode-skill/references/marketplace-addons.md new file mode 100644 index 0000000..82a633b --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/marketplace-addons.md @@ -0,0 +1,565 @@ +# Quicknode Marketplace Add-ons Reference + +Quicknode Marketplace provides enhanced blockchain APIs as add-ons to standard RPC endpoints. Enable add-ons in the Quicknode dashboard to access these methods. + +## Ethereum Token APIs + +### qn_getWalletTokenBalance + +Get all ERC-20 token balances for an address. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_getWalletTokenBalance', + params: [{ + wallet: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + contracts: [] // Empty array for all tokens + }] + }) +}); + +const { result } = await response.json(); +// result.assets: Array of token balances +``` + +**Response:** +```json +{ + "result": { + "owner": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + "assets": [ + { + "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "name": "USD Coin", + "symbol": "USDC", + "decimals": 6, + "balance": "1000000000", + "balanceUSD": "1000.00" + } + ] + } +} +``` + +### qn_getTokenMetadataByContractAddress + +Get token metadata for a specific contract. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_getTokenMetadataByContractAddress', + params: [{ + contract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' + }] + }) +}); +``` + +**Response:** +```json +{ + "result": { + "name": "USD Coin", + "symbol": "USDC", + "decimals": "6", + "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + } +} +``` + +### qn_getTokenMetadataBySymbol + +Get token metadata by symbol. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_getTokenMetadataBySymbol', + params: [{ + symbol: 'USDC' + }] + }) +}); +``` + +## Ethereum NFT APIs + +### qn_fetchNFTs + +Fetch NFTs owned by an address. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_fetchNFTs', + params: [{ + wallet: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + page: 1, + perPage: 10, + contracts: [] // Optional: filter by contracts + }] + }) +}); + +const { result } = await response.json(); +// result.assets: Array of NFTs +// result.totalItems: Total count +// result.pageNumber: Current page +``` + +**Response:** +```json +{ + "result": { + "owner": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + "assets": [ + { + "collectionName": "Bored Ape Yacht Club", + "collectionAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "collectionTokenId": "1234", + "name": "Bored Ape #1234", + "description": "A bored ape", + "imageUrl": "ipfs://...", + "traits": [ + { "trait_type": "Background", "value": "Blue" } + ] + } + ], + "totalItems": 42, + "pageNumber": 1 + } +} +``` + +### qn_fetchNFTCollectionDetails + +Get collection-level details. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_fetchNFTCollectionDetails', + params: [{ + contracts: ['0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D'] + }] + }) +}); +``` + +**Response:** +```json +{ + "result": [ + { + "address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "name": "Bored Ape Yacht Club", + "erc": "erc721", + "totalSupply": "10000" + } + ] +} +``` + +### qn_fetchNFTsByCollection + +Fetch NFTs from a specific collection. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_fetchNFTsByCollection', + params: [{ + collection: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', + tokens: ['1', '2', '3'], // Optional: specific tokens + page: 1, + perPage: 10 + }] + }) +}); +``` + +### qn_verifyNFTsOwner + +Verify NFT ownership. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'qn_verifyNFTsOwner', + params: [{ + wallet: '0xWalletAddress...', + contracts: [ + { + address: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', + tokenIds: ['1234'] + } + ] + }] + }) +}); +``` + +## Solana Add-ons + +### Priority Fee API + +Get recommended priority fees for Solana transactions. + +```javascript +import { createSolanaRpc } from '@solana/kit'; + +const rpc = createSolanaRpc(process.env.QUICKNODE_RPC_URL!); + +const response = await rpc.request('qn_estimatePriorityFees', { + last_n_blocks: 100, + account: 'YourAccountPubkey...' +}).send(); + +// Response includes recommended fees by percentile +// per_compute_unit.low, medium, high, extreme +``` + +**Response:** +```json +{ + "result": { + "per_compute_unit": { + "low": 100, + "medium": 1000, + "high": 10000, + "extreme": 100000 + }, + "per_transaction": { + "low": 1000, + "medium": 10000, + "high": 100000, + "extreme": 1000000 + } + } +} +``` + +### DAS API (Digital Asset Standard) + +Comprehensive API for querying Solana digital assets — standard NFTs, compressed NFTs, fungible tokens, MPL Core Assets, and Token 2022 Assets. Requires the Metaplex DAS API add-on enabled on your endpoint. + +**Docs:** https://www.quicknode.com/docs/solana/solana-das-api + +```javascript +// Get assets by owner +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByOwner', + params: { ownerAddress: 'WalletPubkey...', limit: 10 } + }) +}); + +// Get single asset details +const asset = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAsset', + params: { id: 'AssetMintAddress...' } + }) +}); + +// Search assets with filters +const search = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'searchAssets', + params: { ownerAddress: 'WalletPubkey...', compressed: true, limit: 10 } + }) +}); +``` + +**Available methods:** `getAsset`, `getAssets`, `getAssetProof`, `getAssetProofs`, `getAssetsByAuthority`, `getAssetsByCreator`, `getAssetsByGroup`, `getAssetsByOwner`, `getAssetSignatures`, `getTokenAccounts`, `getNftEditions`, `searchAssets` + +See [solana-das-api-reference.md](solana-das-api-reference.md) for complete DAS API documentation with all methods, parameters, and examples. + +### Metis Jupiter API + +Access Jupiter DEX aggregator for swaps via REST endpoints on your QuickNode Solana endpoint. + +> **Endpoint:** Set `QUICKNODE_METIS_URL` to your QuickNode Metis endpoint (e.g., `https://jupiter-swap-api.quiknode.pro/YOUR_TOKEN`). Enable the Metis - Jupiter V6 Swap API add-on in your QuickNode dashboard. Do not use the public Jupiter API for production — it has lower rate limits and no SLA. + +**Docs:** https://www.quicknode.com/docs/solana/metis-overview + +```javascript +// Get swap quote (GET request) +const quoteUrl = new URL(`${process.env.QUICKNODE_METIS_URL}/quote`); +quoteUrl.searchParams.set('inputMint', 'So11111111111111111111111111111111111111112'); // SOL +quoteUrl.searchParams.set('outputMint', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // USDC +quoteUrl.searchParams.set('amount', '1000000000'); // 1 SOL in lamports +quoteUrl.searchParams.set('slippageBps', '50'); // 0.5% slippage + +const quoteResponse = await fetch(quoteUrl.toString()); +const quote = await quoteResponse.json(); + +// Execute swap (POST request) +const swapResponse = await fetch(`${process.env.QUICKNODE_METIS_URL}/swap`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + userPublicKey: 'YourPubkey...', + quoteResponse: quote + }) +}); + +const { swapTransaction, lastValidBlockHeight } = await swapResponse.json(); +// swapTransaction is a serialized transaction ready for signing and sending +``` + +**Using the Jupiter SDK:** + +```typescript +import { createJupiterApiClient } from '@jup-ag/api'; + +const jupiterApi = createJupiterApiClient({ + basePath: `${process.env.QUICKNODE_METIS_URL}` +}); + +const quote = await jupiterApi.quoteGet({ + inputMint: 'So11111111111111111111111111111111111111112', + outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + amount: 1000000000, + slippageBps: 50 +}); + +const swapResult = await jupiterApi.swapPost({ + swapRequest: { + quoteResponse: quote, + userPublicKey: 'YourPubkey...' + } +}); +``` + +### Yellowstone gRPC + +High-performance streaming for Solana data. + +```javascript +// Configure in endpoint settings +// Use gRPC client to connect + +const client = new YellowstoneClient({ + endpoint: 'YOUR_GRPC_ENDPOINT', + token: process.env.QUICKNODE_API_KEY! +}); + +// Subscribe to account updates +const stream = client.subscribe({ + accounts: { + accountIds: ['AccountPubkey...'] + } +}); + +stream.on('data', (update) => { + console.log('Account updated:', update); +}); +``` + +For full Yellowstone gRPC documentation including all filter types, subscription examples, and multi-language setup, see [yellowstone-grpc-reference.md](yellowstone-grpc-reference.md). + +### Jito Bundles + +MEV protection and bundle submission. + +```javascript +// Submit bundle +const bundleResult = await rpc.request('sendBundle', { + transactions: [ + 'Base64EncodedTx1...', + 'Base64EncodedTx2...' + ] +}).send(); + +// Get bundle status +const status = await rpc.request('getBundleStatuses', { + bundleIds: [bundleResult.bundleId] +}).send(); +``` + +## EVM Trace & Debug APIs + +### trace_call + +Trace a call without executing. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'trace_call', + params: [ + { + to: '0xContractAddress...', + data: '0xFunctionSelector...' + }, + ['trace'], + 'latest' + ] + }) +}); +``` + +### trace_transaction + +Get execution trace for a transaction. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'trace_transaction', + params: ['0xTransactionHash...'] + }) +}); +``` + +### debug_traceTransaction + +Detailed transaction debugging. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'debug_traceTransaction', + params: [ + '0xTransactionHash...', + { tracer: 'callTracer' } + ] + }) +}); +``` + +## Archive Data + +Access historical blockchain state. + +```javascript +// Get balance at specific block +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'eth_getBalance', + params: [ + '0xAddress...', + '0xF4240' // Block 1,000,000 + ] + }) +}); + +// Call contract at historical block +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + method: 'eth_call', + params: [ + { + to: '0xContract...', + data: '0xFunctionSelector...' + }, + '0xF4240' // Block 1,000,000 + ] + }) +}); +``` + +## Using with Quicknode SDK + +```typescript +import { Core } from '@quicknode/sdk'; + +const core = new Core({ + endpointUrl: process.env.QUICKNODE_RPC_URL!, +}); + +// Token API +const tokenBalances = await core.client.qn_getWalletTokenBalance({ + wallet: '0x...', + contracts: [] +}); + +// NFT API +const nfts = await core.client.qn_fetchNFTs({ + wallet: '0x...', + page: 1, + perPage: 10 +}); + +// Collection details +const collection = await core.client.qn_fetchNFTCollectionDetails({ + contracts: ['0x...'] +}); +``` + +## Add-on Availability by Chain + +| Add-on | Ethereum | Polygon | Arbitrum | Base | Solana | +|--------|----------|---------|----------|------|--------| +| Token API | Yes | - | - | - | - | +| NFT API | Yes | - | - | - | DAS | +| Trace API | Yes | Yes | Yes | Yes | - | +| Debug API | Yes | Yes | Yes | Yes | - | +| Archive | Yes | Yes | Yes | Yes | - | +| Priority Fee | - | - | - | - | Yes | +| Jupiter/Metis | - | - | - | - | Yes | +| Yellowstone | - | - | - | - | Yes | +| Jito | - | - | - | - | Yes | + +## Rate Limits + +Add-on methods consume credits based on complexity: + +| Method Type | Credits | +|-------------|---------| +| Token balance | 50 | +| NFT fetch | 100 | +| Collection details | 50 | +| Trace call | 200 | +| Debug trace | 500 | +| Archive query | 100 | + +## Documentation + +- **Marketplace**: https://marketplace.quicknode.com/ +- **Token API**: https://www.quicknode.com/docs/ethereum/qn_getWalletTokenBalance +- **NFT API**: https://www.quicknode.com/docs/ethereum/qn_fetchNFTs +- **Solana Add-ons**: https://www.quicknode.com/docs/solana +- **Metis Jupiter API**: https://www.quicknode.com/docs/solana/metis-overview +- **Trace API**: https://www.quicknode.com/docs/ethereum/trace_call +- **DAS API**: https://www.quicknode.com/docs/solana/solana-das-api +- **Guides**: https://www.quicknode.com/guides/tags/marketplace diff --git a/plugins/quicknode/skills/quicknode-skill/references/mpp-reference.md b/plugins/quicknode/skills/quicknode-skill/references/mpp-reference.md new file mode 100644 index 0000000..ecd40b8 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/mpp-reference.md @@ -0,0 +1,258 @@ +# MPP Reference + +MPP (Machine Payments Protocol) enables pay-per-request RPC access via stablecoin micropayments using IETF Payment Authentication headers. No API key required. + +## Overview + +| Property | Value | +|----------|-------| +| **Protocol** | IETF Payment Authentication (WWW-Authenticate / Authorization / Payment-Receipt) | +| **Payment Methods** | Varies by network. See [Payment Networks](#payment-networks) | +| **Authentication** | None required (payment headers handle auth) | +| **Chains** | 140+ (same as Quicknode RPC network) | +| **Base URL** | `https://mpp.quicknode.com` | +| **Use Cases** | AI agents, pay-as-you-go, simple integrations, high-volume sessions | + +## How It Works + +1. **Send request** — POST to `/:network/*` (charge) or `/session/:network/*` (session) +2. **Receive challenge** — Server returns 402 with `WWW-Authenticate: Payment` header +3. **Sign payment** — Client signs token transfer and retries with `Authorization: Payment` header +4. **Receive response** — Server returns result with `Payment-Receipt` header + +## Intent Types + +| Intent | Cost | Endpoint | Best For | +|--------|------|----------|----------| +| **Charge** | $0.001/request (1,000 atomic units) | `POST /:network/*` | Simple integrations, low volume | +| **Session** | $0.00001/request ($10/1M requests) | `POST /session/:network/*` | High volume, agents, metered usage | + +### Charge + +One on-chain transaction per request. No session state, no escrow. The `mppx` SDK handles the 402 challenge and payment signing automatically. + +### Session + +Payment channels for high-frequency access: +1. Client deposits funds into escrow contract (~500ms initial setup) +2. Each request sends a cumulative EIP-712 signed voucher (off-chain) +3. Server verifies with `ecrecover` — no RPC or DB lookup +4. Settlement is batched when the server closes the channel +5. Unused deposit is refunded on channel close + +## Key Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/:network/*` | POST | Charge-based JSON-RPC and REST proxy | +| `/session/:network/*` | POST | Session-based JSON-RPC and REST proxy | +| `/llms.txt` | GET | Machine-readable documentation | + +Replace `:network` with slugs like `tempo-mainnet`, `ethereum-mainnet`, `solana-mainnet`, etc. + +## Payment Networks + +| Network | Chain ID | Token | Charge | Session | +|---------|----------|-------|:---:|:---:| +| Tempo Testnet | 42431 | PathUSD | Yes | Yes | +| Tempo Mainnet | 4217 | PathUSD, USDC.e | Yes | Yes | +| Solana Mainnet | — | USDC | Yes | No | + +The payment network is independent of the chain you query. For example, you can pay with PathUSD on Tempo and query Ethereum, Solana, or any other supported chain. + +## Testnet Caps + +Testnet caps are limited to 10,000 requests per intent per wallet (charge and session counted separately). After reaching a cap, switch to mainnet for production usage. + +## Rate Limits + +1,000 requests per 10 seconds per IP:network combination. + +## Setup + +### TypeScript (Tempo — Charge) + +```bash +npm install mppx viem +``` + +```typescript +import { Mppx, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +// Polyfills globalThis.fetch — handles 402 challenges automatically +Mppx.create({ + methods: [tempo({ account })], +}) + +// Charge intent ($0.001/req) — payment is transparent +const response = await fetch('https://mpp.quicknode.com/tempo-mainnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_blockNumber', + params: [], + }), +}) + +const { result } = await response.json() +console.log('Block number:', BigInt(result)) +``` + +### TypeScript (Tempo — Session) + +```typescript +import { Mppx, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +// Session-only — payment channels for 100x cheaper requests +Mppx.create({ + methods: [tempo.session({ account })], +}) + +// First request opens channel on-chain (~500ms), +// subsequent requests use off-chain vouchers (microseconds) +const response = await fetch('https://mpp.quicknode.com/session/tempo-mainnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_blockNumber', + params: [], + }), +}) + +console.log(await response.json()) +``` + +### Solana + +```bash +npm install solana-mpp mppx @solana/kit @solana/spl-token +``` + +```typescript +import { Mppx, solana } from 'solana-mpp/client' +import { createKeyPairFromBytes } from '@solana/kit' +import { readFileSync } from 'fs' +import { homedir } from 'os' + +const keypairFile = readFileSync( + process.env.SOLANA_KEYPAIR_PATH ?? `${homedir()}/.config/solana/id.json`, + 'utf-8' +) +const keypair = await createKeyPairFromBytes( + new Uint8Array(JSON.parse(keypairFile)) +) + +Mppx.create({ methods: [solana({ wallet: keypair })] }) + +const response = await fetch('https://mpp.quicknode.com/solana-mainnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getSlot', + params: [], + }), +}) + +const { result } = await response.json() +console.log('Slot:', result) +``` + +### Manual Payment Handling (No Polyfill) + +```typescript +const mppx = Mppx.create({ + polyfill: false, + methods: [tempo()], +}) + +// Step 1: Initial request gets 402 +const response = await fetch('https://mpp.quicknode.com/tempo-mainnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), +}) + +if (response.status === 402) { + // Step 2: Create credential from 402 challenge + const credential = await mppx.createCredential(response, { + account: privateKeyToAccount('0xYOUR_PRIVATE_KEY'), + }) + + // Step 3: Retry with Authorization header + const paidResponse = await fetch('https://mpp.quicknode.com/tempo-mainnet', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: credential, + }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), + }) +} +``` + +### CLI + +```bash +npm install -g mppx + +mppx account create +mppx -X POST -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \ + https://mpp.quicknode.com/tempo-mainnet +``` + +Environment variables: `MPPX_PRIVATE_KEY`, `MPPX_ACCOUNT` + +## Payment Receipts + +```typescript +import { Receipt } from 'mppx' + +const receipt = Receipt.fromResponse(response) +console.log(receipt.status) // "success" +console.log(receipt.reference) // tx hash (charge) or channelId (session) +``` + +## Error Responses + +| Status | Code | Meaning | +|--------|------|---------| +| 402 | (challenge) | Payment required; see `WWW-Authenticate: Payment` header | +| 403 | `lifetime_limit_reached` | Testnet lifetime request cap exceeded; switch to mainnet | +| 404 | `unsupported_network` | Network slug not found | +| 429 | `rate_limit_exceeded` | 1,000 req/10s limit exceeded per IP:network | +| 503 | `mpp_not_configured` | MPP unavailable in this environment | + +## Best Practices + +1. **Use session intents for high volume** — 100x cheaper than charge at scale ($0.00001 vs $0.001 per request). +2. **Use the polyfill for simplicity** — `Mppx.create()` patches `globalThis.fetch` so all fetch calls handle 402 automatically. +3. **Parse receipts** — Use `Receipt.fromResponse()` to verify payment status and get transaction references. +4. **Multi-service agents** — MPP works across any MPP-enabled service, so one wallet and protocol handles Quicknode RPC, LLM providers, and other APIs. + +## NPM Packages + +- **mppx** — Official TypeScript SDK (client, server, middleware, CLI) +- **solana-mpp** — Solana payment method (client, server) +- **viem** — Ethereum/Tempo utilities (peer dependency) +- **@solana/kit** — Solana web3 (peer dependency) + +## Documentation + +- **MPP Platform**: https://mpp.quicknode.com +- **MPP Documentation (llms.txt)**: https://mpp.quicknode.com/llms.txt +- **MPP Docs**: https://mpp.dev +- **MPP Spec (IETF)**: https://datatracker.ietf.org/doc/draft-ryan-httpauth-payment/ +- **Tempo Docs**: https://docs.tempo.xyz diff --git a/plugins/quicknode/skills/quicknode-skill/references/rpc-reference.md b/plugins/quicknode/skills/quicknode-skill/references/rpc-reference.md new file mode 100644 index 0000000..f4b93b8 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/rpc-reference.md @@ -0,0 +1,509 @@ +# RPC Endpoints Reference + +Quicknode provides low-latency JSON-RPC, WebSocket, and REST endpoints for 80+ blockchain networks with built-in authentication, global load balancing, and per-method documentation. + +## Overview + +| Property | Value | +|----------|-------| +| **Protocol** | JSON-RPC 2.0 (HTTP + WebSocket), REST (Beacon Chain) | +| **Chains** | 80+ networks (EVM, Solana, Bitcoin, and more) | +| **Authentication** | Token in URL path, optional JWT or IP allowlisting | +| **EVM Libraries** | ethers.js, viem, web3.js | +| **Solana Libraries** | @solana/kit, @solana/web3.js | +| **Bitcoin** | Raw JSON-RPC via `fetch` | +| **Endpoint Format** | `https://{name}.{network}.quiknode.pro/{token}/` | +| **WebSocket Format** | `wss://{name}.{network}.quiknode.pro/{token}/` | +| **Per-Method Docs** | `https://www.quicknode.com/docs/{chain}/{method}` | + +## Connection Setup + +### EVM Chains + +```typescript +// ethers.js — HTTP +import { JsonRpcProvider } from 'ethers'; +const provider = new JsonRpcProvider(process.env.QUICKNODE_RPC_URL!); + +// ethers.js — WebSocket +import { WebSocketProvider } from 'ethers'; +const wsProvider = new WebSocketProvider(process.env.QUICKNODE_WSS_URL!); + +// viem — HTTP +import { createPublicClient, http } from 'viem'; +import { mainnet } from 'viem/chains'; +const client = createPublicClient({ + chain: mainnet, + transport: http(process.env.QUICKNODE_RPC_URL!), +}); + +// viem — WebSocket +import { createPublicClient, webSocket } from 'viem'; +import { mainnet } from 'viem/chains'; +const wsClient = createPublicClient({ + chain: mainnet, + transport: webSocket(process.env.QUICKNODE_WSS_URL!), +}); +``` + +### Solana + +```typescript +// @solana/kit — HTTP +import { createSolanaRpc } from '@solana/kit'; +const rpc = createSolanaRpc(process.env.QUICKNODE_RPC_URL!); + +// @solana/kit — WebSocket +import { createSolanaRpcSubscriptions } from '@solana/kit'; +const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.QUICKNODE_WSS_URL!); +``` + +### Bitcoin + +```typescript +// Raw JSON-RPC helper +async function btcRpc(method: string, params: unknown[] = []) { + const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), + }); + const { result, error } = await response.json(); + if (error) throw new Error(`${error.code}: ${error.message}`); + return result; +} +``` + +## EVM RPC Methods + +### Core Methods + +| Category | Methods | +|----------|---------| +| **Account** | `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getAccount`, `eth_getTransactionCount`, `eth_getProof` | +| **Block** | `eth_blockNumber`, `eth_getBlockByHash`, `eth_getBlockByNumber`, `eth_getBlockReceipts`, `eth_getBlockTransactionCountByHash`, `eth_getBlockTransactionCountByNumber` | +| **Transaction** | `eth_getTransactionByHash`, `eth_getTransactionByBlockHashAndIndex`, `eth_getTransactionByBlockNumberAndIndex`, `eth_getTransactionReceipt`, `eth_sendRawTransaction`, `eth_getRawTransactionByHash` | +| **Call & Simulate** | `eth_call`, `eth_estimateGas`, `eth_simulateV1`, `eth_callMany` | +| **Logs & Filters** | `eth_getLogs`, `eth_newFilter`, `eth_newBlockFilter`, `eth_newPendingTransactionFilter`, `eth_getFilterChanges`, `eth_getFilterLogs`, `eth_uninstallFilter` | +| **Gas & Fees** | `eth_gasPrice`, `eth_maxPriorityFeePerGas`, `eth_feeHistory`, `eth_blobBaseFee` | +| **Network** | `eth_chainId`, `eth_syncing`, `net_version`, `net_listening`, `net_peerCount`, `web3_clientVersion`, `web3_sha3` | +| **Subscription** | `eth_subscribe`, `eth_unsubscribe` | + +### Code Examples + +**Get balance:** + +```typescript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_getBalance', + params: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 'latest'], + }), +}); +const { result } = await response.json(); +// result: "0x..." (balance in wei, hex-encoded) +``` + +**Send raw transaction:** + +```typescript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_sendRawTransaction', + params: ['0xSignedTransactionData...'], + }), +}); +const { result } = await response.json(); +// result: "0x..." (transaction hash) +``` + +**Get logs (filter by contract events):** + +```typescript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_getLogs', + params: [{ + fromBlock: '0x118C5E0', + toBlock: '0x118C5FF', + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + topics: [ + '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer + ], + }], + }), +}); +const { result } = await response.json(); +// result: Array of log objects { address, topics, data, blockNumber, transactionHash, ... } +``` + +**Call a contract (read-only):** + +```typescript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_call', + params: [{ + to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + data: '0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045', // balanceOf(address) + }, 'latest'], + }), +}); +const { result } = await response.json(); +// result: ABI-encoded return value +``` + +### Debug, Trace & Extended Namespaces + +| Namespace | Methods | +|-----------|---------| +| **debug** | `debug_traceTransaction`, `debug_traceCall`, `debug_traceBlock`, `debug_traceBlockByHash`, `debug_traceBlockByNumber`, `debug_getBadBlocks`, `debug_storageRangeAt`, `debug_getTrieFlushInterval` | +| **trace** (Erigon) | `trace_block`, `trace_call`, `trace_callMany`, `trace_filter`, `trace_rawTransaction`, `trace_replayBlockTransactions`, `trace_replayTransaction`, `trace_transaction` | +| **erigon** | `erigon_blockNumber`, `erigon_forks`, `erigon_getBlockByTimestamp`, `erigon_getBlockReceiptsByBlockHash`, `erigon_getHeaderByHash`, `erigon_getHeaderByNumber`, `erigon_getLatestLogs`, `erigon_getLogsByHash` | +| **txpool** (Geth) | `txpool_content`, `txpool_contentFrom`, `txpool_inspect`, `txpool_status` | + +### Quicknode Custom Methods (qn_*) + +| Method | Description | +|--------|-------------| +| `qn_getBlockFromTimestamp` | Find block closest to a Unix timestamp | +| `qn_getBlocksInTimestampRange` | List blocks within a timestamp range | +| `qn_getBlockWithReceipts` | Get block data with all transaction receipts | +| `qn_getReceipts` | Batch-fetch receipts for a block | +| `qn_broadcastRawTransaction` | Multi-region transaction broadcast | +| `qn_resolveENS` | Resolve ENS name to address (and reverse) | +| `qn_sendRawTransactionWithWebhook` | Send transaction and receive webhook notification | +| `qn_fetchNFTs` | Fetch NFTs owned by an address | +| `qn_fetchNFTCollectionDetails` | Get collection-level metadata | +| `qn_fetchNFTsByCollection` | Fetch NFTs from a specific collection | +| `qn_getTokenMetadataByContractAddress` | Token metadata by contract | +| `qn_getTokenMetadataBySymbol` | Token metadata by symbol | +| `qn_getWalletTokenBalance` | All ERC-20 balances for a wallet | +| `qn_getWalletTokenTransactions` | Token transfer history for a wallet | +| `qn_getTransactionsByAddress` | Transaction history for an address | +| `qn_getTransfersByNFT` | Transfer history for an NFT | +| `qn_verifyNFTsOwner` | Verify NFT ownership | + +### Beacon Chain REST Endpoints + +Beacon Chain data is accessible via REST endpoints on Ethereum endpoints. + +| Category | Endpoints | +|----------|-----------| +| **Blobs** | `GET /eth/v1/beacon/blob_sidecars/{block_id}`, `GET /eth/v1/beacon/blobs/{block_id}` | +| **Blocks** | `GET /eth/v2/beacon/blocks/{block_id}`, `GET /eth/v1/beacon/blocks/{block_id}/root`, `GET /eth/v1/beacon/headers`, `GET /eth/v1/beacon/headers/{block_id}` | +| **State** | `GET /eth/v1/beacon/states/{state_id}/root`, `GET /eth/v1/beacon/states/{state_id}/fork`, `GET /eth/v1/beacon/states/{state_id}/finality_checkpoints` | +| **Validators** | `GET /eth/v1/beacon/states/{state_id}/validators`, `GET /eth/v1/beacon/states/{state_id}/validators/{validator_id}`, `GET /eth/v1/beacon/states/{state_id}/validator_balances`, `GET /eth/v1/beacon/states/{state_id}/committees`, `GET /eth/v1/beacon/states/{state_id}/sync_committees` | +| **Pending** | `GET /eth/v1/beacon/states/{state_id}/pending_deposits`, `GET /eth/v1/beacon/states/{state_id}/pending_consolidations` | +| **Rewards** | `POST /eth/v1/beacon/rewards/attestations/{epoch}`, `GET /eth/v1/beacon/rewards/blocks/{block_id}`, `POST /eth/v1/beacon/rewards/sync_committee/{block_id}` | +| **Pool** | `GET /eth/v1/beacon/pool/voluntary_exits` | +| **Config** | `GET /eth/v1/beacon/genesis`, `GET /eth/v1/config/deposit_contract`, `GET /eth/v1/config/fork_schedule`, `GET /eth/v1/config/spec` | +| **Validator Duties** | `POST /eth/v1/validator/duties/attester/{epoch}`, `GET /eth/v1/validator/duties/proposer/{epoch}`, `POST /eth/v1/validator/duties/sync/{epoch}`, `GET /eth/v1/validator/blinded_blocks/{slot}`, `GET /eth/v1/validator/sync_committee_contribution` | +| **Events** | `GET /eth/v1/events` (SSE: `head`, `block`, `attestation`, `voluntary_exit`, `finalized_checkpoint`, `chain_reorg`) | +| **Node** | `GET /eth/v1/node/peer_count`, `GET /eth/v1/node/peers`, `GET /eth/v1/node/syncing`, `GET /eth/v1/node/version` | +| **Debug** | `GET /eth/v1/debug/beacon/data_column_sidecars/{block_id}`, `GET /eth/v2/debug/beacon/states/{state_id}` | + +## Solana RPC Methods + +### Standard Methods + +| Category | Methods | +|----------|---------| +| **Account** | `getAccountInfo`, `getMultipleAccounts`, `getProgramAccounts`, `getLargestAccounts`, `getMinimumBalanceForRentExemption` | +| **Balance** | `getBalance`, `getTokenAccountBalance`, `getTokenAccountsByOwner`, `getTokenAccountsByDelegate`, `getTokenLargestAccounts`, `getTokenSupply` | +| **Block** | `getBlock`, `getBlockCommitment`, `getBlockHeight`, `getBlockProduction`, `getBlocks`, `getBlocksWithLimit`, `getBlockTime`, `getFirstAvailableBlock` | +| **Transaction** | `getTransaction`, `getParsedTransaction`, `getTransactionCount`, `getSignaturesForAddress`, `getSignatureStatuses`, `simulateTransaction`, `sendTransaction` | +| **Slot** | `getSlot`, `getSlotLeader`, `getSlotLeaders`, `getHighestSnapshotSlot`, `getMaxRetransmitSlot`, `getMaxShredInsertSlot` | +| **Fees** | `getFeeForMessage`, `getRecentPrioritizationFees` | +| **Epoch & Inflation** | `getEpochInfo`, `getEpochSchedule`, `getInflationGovernor`, `getInflationRate`, `getInflationReward`, `getLeaderSchedule` | +| **Network** | `getClusterNodes`, `getHealth`, `getIdentity`, `getVersion`, `getGenesisHash`, `getSupply`, `getVoteAccounts`, `getStakeMinimumDelegation`, `getRecentPerformanceSamples`, `minimumLedgerSlot` | +| **Utility** | `isBlockhashValid`, `requestAirdrop` (testnet/devnet only) | + +### Code Examples + +**Get balance and account info:** + +```typescript +import { createSolanaRpc } from '@solana/kit'; +import { address } from '@solana/addresses'; + +const rpc = createSolanaRpc(process.env.QUICKNODE_RPC_URL!); + +const balance = await rpc.getBalance(address('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg')).send(); +// balance.value: bigint (lamports) + +const accountInfo = await rpc.getAccountInfo(address('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg'), { + encoding: 'base64', +}).send(); +// accountInfo.value: { data, executable, lamports, owner, rentEpoch } +``` + +**Send transaction:** + +```typescript +import { createSolanaRpc } from '@solana/kit'; + +const rpc = createSolanaRpc(process.env.QUICKNODE_RPC_URL!); + +// Transaction must be signed before sending +const signature = await rpc.sendTransaction(signedTransactionBytes, { + encoding: 'base64', + skipPreflight: false, + preflightCommitment: 'confirmed', +}).send(); +// signature: base-58 encoded transaction signature +``` + +**Get program accounts (with filters):** + +```typescript +import { createSolanaRpc } from '@solana/kit'; + +const rpc = createSolanaRpc(process.env.QUICKNODE_RPC_URL!); + +const accounts = await rpc.getProgramAccounts( + address('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'), + { + encoding: 'base64', + filters: [ + { dataSize: 165n }, // Token account size + { memcmp: { offset: 32n, bytes: 'OwnerPubkeyBase58...' as `${string}`, encoding: 'base58' } }, + ], + } +).send(); +// accounts: Array of { pubkey, account: { data, executable, lamports, owner } } +``` + +### WebSocket Subscriptions + +| Subscription | Description | +|-------------|-------------| +| `accountSubscribe` | Monitor changes to a specific account | +| `programSubscribe` | Monitor all accounts owned by a program | +| `logsSubscribe` | Subscribe to transaction log output | +| `signatureSubscribe` | Track confirmation of a specific transaction | +| `slotSubscribe` | Monitor slot progression | +| `blockSubscribe` | Track new confirmed/finalized blocks | +| `rootSubscribe` | Receive root slot notifications | +| `slotsUpdatesSubscribe` | Detailed slot update notifications | + +```typescript +import { createSolanaRpcSubscriptions } from '@solana/kit'; +import { address } from '@solana/addresses'; + +const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.QUICKNODE_WSS_URL!); + +// Subscribe to account changes +const accountNotifications = await rpcSubscriptions + .accountNotifications(address('AccountPubkey...'), { commitment: 'confirmed' }) + .subscribe({ abortSignal: AbortSignal.timeout(60_000) }); + +for await (const notification of accountNotifications) { + console.log('Account changed:', notification.value.lamports); +} +``` + +### Solana-Specific Add-on Methods + +| Category | Methods | +|----------|---------| +| **Priority Fees** | `qn_estimatePriorityFees` | +| **DAS (Digital Asset Standard)** | `getAsset`, `getAssets`, `getAssetProof`, `getAssetProofs`, `getAssetsByOwner`, `getAssetsByCreator`, `getAssetsByAuthority`, `getAssetsByGroup`, `getAssetSignatures`, `getTokenAccounts`, `getNftEditions`, `searchAssets` | +| **Jito Bundles** | `sendBundle`, `getBundleStatuses`, `getInflightBundleStatuses`, `simulateBundle`, `getTipAccounts`, `getTipFloor`, `getRegions` | +| **Jito Transaction** | `sendTransaction` (Jito-routed) | +| **Metis (Jupiter)** | `/quote`, `/swap`, `/swap-instructions`, `/tokens`, `/price`, `/new-pools`, `/program-id-to-label` | +| **Metis Limit Orders** | `/limit-orders/{pubkey}`, `/limit-orders/create`, `/limit-orders/cancel`, `/limit-orders/fee`, `/limit-orders/history`, `/limit-orders/open` | +| **Metis Pump.fun** | `/pump-fun/quote`, `/pump-fun/swap`, `/pump-fun/swap-instructions` | + +## Bitcoin RPC Methods + +### Standard Methods + +| Category | Methods | +|----------|---------| +| **Blockchain** | `getbestblockhash`, `getblock`, `getblockchaininfo`, `getblockcount`, `getblockhash`, `getblockheader`, `getblockstats`, `getchaintips`, `getchaintxstats` | +| **Transaction** | `getrawtransaction`, `decoderawtransaction`, `decodescript`, `sendrawtransaction`, `gettxout`, `gettxoutproof`, `gettxoutsetinfo`, `testmempoolaccept`, `submitpackage` | +| **Mempool** | `getrawmempool`, `getmempoolancestors`, `getmempooldescendants`, `getmempoolinfo` | +| **Mining & Network** | `getdifficulty`, `getmininginfo`, `estimatesmartfee`, `getconnectioncount`, `getnetworkinfo`, `getmemoryinfo`, `getindexinfo` | +| **Validation** | `validateaddress`, `verifymessage` | + +### Code Examples + +**Get block count and block data:** + +```typescript +// Get current block height +const blockCount = await btcRpc('getblockcount'); +console.log('Block height:', blockCount); + +// Get block hash for a specific height +const blockHash = await btcRpc('getblockhash', [blockCount]); + +// Get full block data (verbosity 2 = include decoded transactions) +const block = await btcRpc('getblock', [blockHash, 2]); +console.log('Block:', { + hash: block.hash, + height: block.height, + time: block.time, + nTx: block.nTx, + size: block.size, +}); +``` + +**Get raw transaction:** + +```typescript +// Get decoded transaction (verbose = true) +const tx = await btcRpc('getrawtransaction', [ + 'txid...', + true, // verbose: return JSON object instead of hex +]); +console.log('Transaction:', { + txid: tx.txid, + size: tx.size, + vout: tx.vout.map((o: any) => ({ value: o.value, address: o.scriptPubKey?.address })), +}); +``` + +### Ordinals, Runes & Blockbook + +| Category | Methods | +|----------|---------| +| **Ordinals** | `ord_getInscription`, `ord_getInscriptions`, `ord_getInscriptionsByBlock`, `ord_getContent`, `ord_getMetadata`, `ord_getChildren`, `ord_getCollections`, `ord_getInscriptionRecursive` | +| **Sats** | `ord_getSat`, `ord_getSatAtIndex`, `ord_getSatRecursive` | +| **Runes** | `ord_getRune`, `ord_getRunes` | +| **Ordinals Utility** | `ord_getBlockHash`, `ord_getBlockInfo`, `ord_getCurrentBlockHash`, `ord_getCurrentBlockHeight`, `ord_getCurrentBlockTime`, `ord_getOutput`, `ord_getStatus`, `ord_getTx` | +| **Quicknode** | `qn_getBlockFromTimestamp`, `qn_getBlocksInTimestampRange` | +| **Blockbook** | `bb_getAddress`, `bb_getBalanceHistory`, `bb_getBlock`, `bb_getBlockHash`, `bb_getTx`, `bb_getTxSpecific`, `bb_getUTXOs`, `bb_getXPUB`, `bb_getTickers`, `bb_getTickersList` | + +## WebSocket Patterns + +### EVM Subscriptions + +| Type | Description | +|------|-------------| +| `newHeads` | New block headers as they are mined | +| `logs` | Log entries matching a filter (address, topics) | +| `newPendingTransactions` | Transaction hashes entering the mempool | +| `syncing` | Node sync status changes | + +```typescript +import { WebSocketProvider } from 'ethers'; + +const wsProvider = new WebSocketProvider(process.env.QUICKNODE_WSS_URL!); + +// Subscribe to new blocks +wsProvider.on('block', (blockNumber) => { + console.log('New block:', blockNumber); +}); + +// Subscribe to pending transactions +wsProvider.on('pending', (txHash) => { + console.log('Pending tx:', txHash); +}); + +// Subscribe to contract events +const filter = { + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], // Transfer +}; +wsProvider.on(filter, (log) => { + console.log('Transfer event:', log); +}); +``` + +### Solana Subscriptions + +See the [Solana WebSocket Subscriptions](#websocket-subscriptions) table above. Use `@solana/kit`'s `createSolanaRpcSubscriptions` for typed subscription handling. + +## Batch Requests + +JSON-RPC supports sending multiple calls in a single HTTP request by wrapping them in an array. This reduces round trips and is ideal for reading multiple pieces of data at once. + +```typescript +const response = await fetch(process.env.QUICKNODE_RPC_URL!, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify([ + { jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }, + { jsonrpc: '2.0', id: 2, method: 'eth_gasPrice', params: [] }, + { jsonrpc: '2.0', id: 3, method: 'eth_getBalance', params: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 'latest'] }, + ]), +}); +const results = await response.json(); +// results: Array of { jsonrpc, id, result } in request order +// results[0].result: block number (hex) +// results[1].result: gas price (hex) +// results[2].result: balance (hex) +``` + +Batch requests work the same way for Bitcoin (`getblockcount`, `getbestblockhash`, etc.) and any JSON-RPC endpoint. Solana also supports batching via the standard JSON-RPC interface. + +## Best Practices + +1. **Use WebSocket for subscriptions** — HTTP polling wastes requests and adds latency. Use `wss://` endpoints for real-time data (new blocks, pending transactions, account changes). +2. **Batch read requests** — Combine multiple `eth_getBalance`, `eth_call`, or similar reads into a single batch request to reduce round trips and credit usage. +3. **Cache immutable data** — Block data, transaction receipts, and finalized results never change. Cache them locally to avoid redundant calls. +4. **Retry with exponential backoff** — On 429 (rate limit) or network errors, retry with increasing delays: 1s, 2s, 4s, up to 30s max. +5. **Use archive endpoints for historical data** — Queries against old blocks require archive mode. Enable it on your Quicknode endpoint if you need `eth_getBalance` at historical blocks or Solana snapshots beyond the current epoch. +6. **Set Solana commitment levels appropriately** — Use `confirmed` for most reads, `finalized` when irreversibility matters (e.g., payment verification), and `processed` only when you need lowest latency and can handle rollbacks. +7. **Consult chain-specific llms.txt for method details** — Each chain has detailed per-method documentation at `https://www.quicknode.com/docs/{chain}/llms.txt` (e.g., `ethereum`, `solana`, `bitcoin`). + +## Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| `Method not found` | Method not available on your plan or endpoint type | Check method availability in the chain docs; some methods require add-ons or archive mode | +| `429 Too Many Requests` | Rate limit exceeded | Implement backoff/retry; batch requests; upgrade plan if persistent | +| `execution reverted` | Smart contract call failed | Check the `to` address, `data` encoding, and block tag; use `eth_estimateGas` first to catch revert reasons | +| Empty `eth_getLogs` result | Block range too narrow, wrong address, or wrong topics | Widen the block range; verify the contract address and topic hashes; check the chain | +| Solana `blockhash expired` | Transaction submitted too late after fetching blockhash | Fetch a fresh blockhash immediately before signing; use `isBlockhashValid` to check | +| Bitcoin `Work queue depth exceeded` | Too many concurrent requests | Reduce concurrency; add request queuing with rate limiting | +| WebSocket disconnects | Idle timeout or server maintenance | Implement automatic reconnection with exponential backoff; send periodic pings | + +## Documentation + +### Chain-Specific Docs + +- **Ethereum**: https://www.quicknode.com/docs/ethereum +- **Solana**: https://www.quicknode.com/docs/solana +- **Bitcoin**: https://www.quicknode.com/docs/bitcoin +- **Polygon**: https://www.quicknode.com/docs/polygon +- **Arbitrum**: https://www.quicknode.com/docs/arbitrum +- **Base**: https://www.quicknode.com/docs/base +- **Full Chain List**: https://www.quicknode.com/chains + +### LLM-Optimized Documentation (llms.txt) + +- **Platform Overview**: https://www.quicknode.com/llms.txt +- **Docs Index**: https://www.quicknode.com/docs/llms.txt +- **Ethereum Methods**: https://www.quicknode.com/docs/ethereum/llms.txt +- **Solana Methods**: https://www.quicknode.com/docs/solana/llms.txt +- **Bitcoin Methods**: https://www.quicknode.com/docs/bitcoin/llms.txt +- **Pattern**: `https://www.quicknode.com/docs/{chain}/llms.txt` + +### Guides + +- **Quicknode Guides**: https://www.quicknode.com/guides + +### Related References + +- [SDK Reference](sdk-reference.md) — Quicknode SDK with typed client methods +- [Marketplace Add-ons](marketplace-addons.md) — Token API, NFT API, DAS, Jito, trace/debug +- [Yellowstone gRPC](yellowstone-grpc-reference.md) — Solana Geyser streaming via gRPC diff --git a/plugins/quicknode/skills/quicknode-skill/references/sdk-reference.md b/plugins/quicknode/skills/quicknode-skill/references/sdk-reference.md new file mode 100644 index 0000000..dce37db --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/sdk-reference.md @@ -0,0 +1,419 @@ +# Quicknode SDK Reference + +The Quicknode SDK provides a type-safe JavaScript/TypeScript client for interacting with Quicknode services. + +## Installation + +```bash +npm install @quicknode/sdk +``` + +## Core Setup + +```typescript +import { Core } from '@quicknode/sdk'; + +const core = new Core({ + endpointUrl: process.env.QUICKNODE_RPC_URL!, +}); +``` + +## Configuration Options + +```typescript +const core = new Core({ + // Required: Your Quicknode endpoint URL + endpointUrl: process.env.QUICKNODE_RPC_URL!, + + // Optional: Chain ID (auto-detected from endpoint) + chain: 1, + + // Optional: Request timeout in milliseconds + timeout: 30000, + + // Optional: Custom fetch implementation + fetch: customFetch, + + // Optional: Custom headers + headers: { + 'X-Custom-Header': 'value' + } +}); +``` + +## Standard RPC Methods + +### Ethereum/EVM + +```typescript +// Get balance +const balance = await core.client.getBalance({ + address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' +}); + +// Get block +const block = await core.client.getBlock({ + blockNumber: 'latest' +}); + +// Get transaction +const tx = await core.client.getTransaction({ + hash: '0xTransactionHash...' +}); + +// Get transaction receipt +const receipt = await core.client.getTransactionReceipt({ + hash: '0xTransactionHash...' +}); + +// Call contract +const result = await core.client.call({ + to: '0xContractAddress...', + data: '0xFunctionSelector...' +}); + +// Estimate gas +const gas = await core.client.estimateGas({ + from: '0xSender...', + to: '0xRecipient...', + value: '0x0' +}); + +// Get logs +const logs = await core.client.getLogs({ + address: '0xContractAddress...', + fromBlock: 18000000, + toBlock: 'latest', + topics: ['0xEventSignature...'] +}); +``` + +## Token API Methods + +Requires Token API add-on enabled. + +### qn_getWalletTokenBalance + +```typescript +const tokenBalances = await core.client.qn_getWalletTokenBalance({ + wallet: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + contracts: [] // Empty for all tokens, or specify addresses +}); + +console.log('Tokens:', tokenBalances.assets); +``` + +### qn_getTokenMetadataByContractAddress + +```typescript +const metadata = await core.client.qn_getTokenMetadataByContractAddress({ + contract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' +}); + +console.log(`${metadata.name} (${metadata.symbol})`); +``` + +### qn_getTokenMetadataBySymbol + +```typescript +const metadata = await core.client.qn_getTokenMetadataBySymbol({ + symbol: 'USDC' +}); +``` + +## NFT API Methods + +Requires NFT API add-on enabled. + +### qn_fetchNFTs + +```typescript +const nfts = await core.client.qn_fetchNFTs({ + wallet: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + page: 1, + perPage: 10, + contracts: [] // Optional: filter by contracts +}); + +console.log(`Total NFTs: ${nfts.totalItems}`); +nfts.assets.forEach(nft => { + console.log(`${nft.name} - ${nft.collectionName}`); +}); +``` + +### qn_fetchNFTCollectionDetails + +```typescript +const collections = await core.client.qn_fetchNFTCollectionDetails({ + contracts: [ + '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', + '0x60E4d786628Fea6478F785A6d7e704777c86a7c6' + ] +}); + +collections.forEach(collection => { + console.log(`${collection.name}: ${collection.totalSupply} items`); +}); +``` + +### qn_fetchNFTsByCollection + +```typescript +const collectionNFTs = await core.client.qn_fetchNFTsByCollection({ + collection: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', + tokens: ['1', '2', '3'], // Optional: specific token IDs + page: 1, + perPage: 10 +}); +``` + +### qn_verifyNFTsOwner + +```typescript +const verification = await core.client.qn_verifyNFTsOwner({ + wallet: '0xOwnerAddress...', + contracts: [ + { + address: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', + tokenIds: ['1234', '5678'] + } + ] +}); + +console.log('Owns NFTs:', verification.owner); +``` + +## Multi-Chain Setup + +```typescript +import { Core } from '@quicknode/sdk'; + +// Create clients for multiple chains +const chains = { + ethereum: new Core({ + endpointUrl: 'https://eth-endpoint.quiknode.pro/KEY/' + }), + polygon: new Core({ + endpointUrl: 'https://polygon-endpoint.quiknode.pro/KEY/' + }), + arbitrum: new Core({ + endpointUrl: 'https://arbitrum-endpoint.quiknode.pro/KEY/' + }), + base: new Core({ + endpointUrl: 'https://base-endpoint.quiknode.pro/KEY/' + }) +}; + +// Use appropriate chain +async function getBalance(chain: keyof typeof chains, address: string) { + return chains[chain].client.getBalance({ address }); +} + +const ethBalance = await getBalance('ethereum', '0x...'); +const polyBalance = await getBalance('polygon', '0x...'); +``` + +## Custom RPC Calls + +For methods not directly exposed by the SDK: + +```typescript +// Generic request method +const result = await core.client.request({ + method: 'trace_transaction', + params: ['0xTransactionHash...'] +}); + +// Or use raw fetch +const response = await fetch(core.config.endpointUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'debug_traceTransaction', + params: ['0xTransactionHash...', { tracer: 'callTracer' }] + }) +}); +``` + +## Error Handling + +```typescript +import { Core, QuicknodeError } from '@quicknode/sdk'; + +const core = new Core({ + endpointUrl: process.env.QUICKNODE_RPC_URL! +}); + +try { + const balance = await core.client.getBalance({ + address: '0x...' + }); +} catch (error) { + if (error instanceof QuicknodeError) { + console.error('Quicknode Error:', error.message); + console.error('Code:', error.code); + console.error('Details:', error.data); + } else { + throw error; + } +} +``` + +## TypeScript Types + +```typescript +import type { + GetBalanceParams, + GetBalanceResult, + GetBlockParams, + GetBlockResult, + QnFetchNFTsParams, + QnFetchNFTsResult, + QnGetWalletTokenBalanceParams, + QnGetWalletTokenBalanceResult +} from '@quicknode/sdk'; + +// Use types for better IDE support +const params: QnFetchNFTsParams = { + wallet: '0x...', + page: 1, + perPage: 10 +}; + +const result: QnFetchNFTsResult = await core.client.qn_fetchNFTs(params); +``` + +## Common Patterns + +### Batch Balance Check + +```typescript +async function getMultipleBalances(addresses: string[]) { + const balancePromises = addresses.map(address => + core.client.getBalance({ address }) + ); + + const balances = await Promise.all(balancePromises); + + return addresses.map((address, index) => ({ + address, + balance: balances[index] + })); +} +``` + +### Token Portfolio + +```typescript +async function getPortfolio(wallet: string) { + const [ethBalance, tokens, nfts] = await Promise.all([ + core.client.getBalance({ address: wallet }), + core.client.qn_getWalletTokenBalance({ wallet, contracts: [] }), + core.client.qn_fetchNFTs({ wallet, page: 1, perPage: 100 }) + ]); + + return { + eth: ethBalance, + tokens: tokens.assets, + nfts: nfts.assets, + nftCount: nfts.totalItems + }; +} +``` + +### Retry with Backoff + +```typescript +async function withRetry( + fn: () => Promise, + maxRetries = 3, + baseDelay = 1000 +): Promise { + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + return await fn(); + } catch (error) { + if (attempt === maxRetries - 1) throw error; + + const delay = baseDelay * Math.pow(2, attempt); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + throw new Error('Max retries exceeded'); +} + +// Usage +const balance = await withRetry(() => + core.client.getBalance({ address: '0x...' }) +); +``` + +### Caching Layer + +```typescript +const cache = new Map(); +const CACHE_TTL = 60000; // 1 minute + +async function cachedCall( + key: string, + fn: () => Promise +): Promise { + const cached = cache.get(key); + + if (cached && Date.now() - cached.timestamp < CACHE_TTL) { + return cached.data as T; + } + + const data = await fn(); + cache.set(key, { data, timestamp: Date.now() }); + return data; +} + +// Usage +const balance = await cachedCall( + `balance:${address}`, + () => core.client.getBalance({ address }) +); +``` + +## Browser Usage + +```html + +``` + +## Node.js Best Practices + +```typescript +import { Core } from '@quicknode/sdk'; + +// Use environment variables for API keys +const core = new Core({ + endpointUrl: process.env.QUICKNODE_ENDPOINT_URL! +}); + +// Graceful shutdown +process.on('SIGTERM', async () => { + // Cleanup if needed + process.exit(0); +}); +``` + +## Documentation + +- **SDK Overview**: https://www.quicknode.com/docs/quicknode-sdk +- **npm Package**: https://www.npmjs.com/package/@quicknode/sdk +- **Guides**: https://www.quicknode.com/guides/tags/quicknode-sdk diff --git a/plugins/quicknode/skills/quicknode-skill/references/solana-das-api-reference.md b/plugins/quicknode/skills/quicknode-skill/references/solana-das-api-reference.md new file mode 100644 index 0000000..afeb163 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/solana-das-api-reference.md @@ -0,0 +1,532 @@ +# Solana DAS API (Digital Asset Standard) Reference + +The Metaplex Digital Asset Standard (DAS) API is a comprehensive service for querying Solana digital assets efficiently. It supports standard and compressed NFTs, fungible tokens, MPL Core Assets, and Token 2022 Assets. + +**Docs:** https://www.quicknode.com/docs/solana/solana-das-api + +## Prerequisites + +Enable the **Metaplex Digital Asset Standard (DAS) API** add-on on your QuickNode Solana endpoint via the [Marketplace](https://marketplace.quicknode.com/). + +## Methods Overview + +| Method | Description | +|--------|-------------| +| `getAsset` | Get metadata for a single asset | +| `getAssets` | Get metadata for multiple assets in one call | +| `getAssetProof` | Get Merkle proof for a compressed asset | +| `getAssetProofs` | Get Merkle proofs for multiple compressed assets | +| `getAssetsByAuthority` | List assets controlled by an authority | +| `getAssetsByCreator` | List assets by creator address | +| `getAssetsByGroup` | List assets by group (e.g., collection) | +| `getAssetsByOwner` | List assets owned by a wallet | +| `getAssetSignatures` | Get transaction signatures for compressed assets | +| `getTokenAccounts` | List token accounts by mint or owner | +| `getNftEditions` | Get edition details of a master NFT | +| `searchAssets` | Search assets with flexible filters | + +## Supported Asset Types + +- **Standard NFTs** — traditional Solana NFTs +- **Compressed NFTs (cNFTs)** — Merkle tree-based, cost-efficient NFTs +- **Fungible Tokens** — SPL tokens +- **MPL Core Assets** — single-account design NFTs +- **Token 2022 Assets** — tokens using the Token Extensions program + +## getAsset + +Retrieve metadata for a single asset by its mint address. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAsset', + params: { + id: '9ARngHhVaCtH5JFieRdSS5Y8cdZk2TMF4tfGSWFB9iSK', + options: { + showFungible: true, + showCollectionMetadata: true + } + } + }) +}); +const { result } = await response.json(); +// result.content — metadata (name, description, image, attributes) +// result.ownership — owner, delegate, frozen status +// result.compression — tree, leaf, proof info (if compressed) +// result.royalty — royalty model, basis points, creators +// result.creators — array of creator addresses with verified status +// result.supply — edition/print supply info +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | string | Yes | The asset mint address | +| `options.showFungible` | boolean | No | Include fungible token info | +| `options.showCollectionMetadata` | boolean | No | Include collection metadata | +| `options.showUnverifiedCollections` | boolean | No | Include unverified collections | + +## getAssets + +Fetch metadata for multiple assets in a single request. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssets', + params: { + ids: [ + '9ARngHhVaCtH5JFieRdSS5Y8cdZk2TMF4tfGSWFB9iSK', + 'BwJHge5FmE5RBkmWPoKzCWwxZFXsnqCMKHiiibXPJias' + ] + } + }) +}); +const { result } = await response.json(); +// result.items — array of asset metadata objects +``` + +## getAssetProof + +Get the Merkle proof for a compressed asset. Required for transferring or modifying compressed NFTs on-chain. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetProof', + params: { + id: 'D85MZkvir9yQZFDHt8U2ZmS7D3LXKdiSjvw2MBdscJJa' + } + }) +}); +const { result } = await response.json(); +// result.root — Merkle tree root hash +// result.proof — array of proof nodes +// result.node_index — index in the tree +// result.leaf — leaf hash +// result.tree_id — Merkle tree address +``` + +## getAssetProofs + +Retrieve Merkle proofs for multiple compressed assets in one call. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetProofs', + params: { + ids: [ + 'D85MZkvir9yQZFDHt8U2ZmS7D3LXKdiSjvw2MBdscJJa', + 'AnotherCompressedAssetMint...' + ] + } + }) +}); +const { result } = await response.json(); +// result — object keyed by asset ID, each containing root, proof, node_index, leaf, tree_id +``` + +## getAssetsByOwner + +List all digital assets owned by a wallet address. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByOwner', + params: { + ownerAddress: 'E645TckHQnDcavVv92Etc6xSWQaq8zzPtPRGBheviRAk', + limit: 10, + sortBy: { sortBy: 'recent_action', sortDirection: 'desc' }, + options: { + showFungible: true, + showCollectionMetadata: true + } + } + }) +}); +const { result } = await response.json(); +// result.total — total assets owned +// result.items — array of asset metadata objects +// result.cursor — use in next request for pagination +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `ownerAddress` | string | Yes | Wallet address | +| `limit` | integer | No | Max results per page | +| `page` | integer | No | Page number (page-based pagination) | +| `cursor` | string | No | Cursor from previous response (cursor-based pagination) | +| `before` / `after` | string | No | Range-based pagination | +| `sortBy` | object | No | `{ sortBy: "created" \| "recent_action" \| "id" \| "none", sortDirection: "asc" \| "desc" }` | +| `options.showFungible` | boolean | No | Include fungible tokens | +| `options.showCollectionMetadata` | boolean | No | Include collection metadata | + +## getAssetsByCreator + +List assets created by a specific address. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByCreator', + params: { + creatorAddress: '3pMvTLUA9NzZQd4gi725p89mvND1wRNQM3C8XEv1hTdA', + limit: 10 + } + }) +}); +const { result } = await response.json(); +// result.total, result.items, result.cursor +``` + +## getAssetsByGroup + +List assets by group identifier (e.g., collection address). + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByGroup', + params: { + groupKey: 'collection', + groupValue: 'CollectionMintAddress...', + limit: 10 + } + }) +}); +const { result } = await response.json(); +// result.total, result.items, result.cursor +``` + +## getAssetsByAuthority + +List assets controlled by a specific authority. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByAuthority', + params: { + authorityAddress: 'AuthorityPubkey...', + limit: 10 + } + }) +}); +const { result } = await response.json(); +// result.total, result.items, result.cursor +``` + +## getAssetSignatures + +Get transaction signatures associated with a compressed asset. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetSignatures', + params: { + id: 'CompressedAssetMint...', + limit: 10 + } + }) +}); +const { result } = await response.json(); +// result.items — array of transaction signature objects +``` + +## getTokenAccounts + +List token accounts and balances by mint address or owner address. Useful for finding all holders of a token or all tokens held by a wallet. + +```javascript +// By mint address — find holders of a token +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getTokenAccounts', + params: { + mintAddress: 'So11111111111111111111111111111111111111112', + limit: 10 + } + }) +}); +const { result } = await response.json(); +// result.total — total token accounts +// result.token_accounts — array of accounts with: +// address, mint, owner, amount, delegated_amount, frozen +``` + +```javascript +// By owner address — find all tokens held by a wallet +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getTokenAccounts', + params: { + ownerAddress: 'WalletPubkey...', + limit: 10 + } + }) +}); +``` + +## getNftEditions + +Retrieve edition details for a master NFT, including all printed editions. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getNftEditions', + params: { + mintAddress: 'MasterEditionMint...', + limit: 10 + } + }) +}); +const { result } = await response.json(); +// result.items — array of edition details +``` + +## searchAssets + +Search for assets using flexible filter criteria. The most powerful query method in the DAS API. + +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'searchAssets', + params: { + ownerAddress: 'WalletPubkey...', + compressed: true, + limit: 10, + sortBy: { sortBy: 'recent_action', sortDirection: 'desc' } + } + }) +}); +const { result } = await response.json(); +// result.total, result.items, result.cursor +``` + +**Search Filter Parameters:** + +| Parameter | Type | Description | +|-----------|------|-------------| +| `ownerAddress` | string | Filter by asset owner | +| `creatorAddress` | string | Filter by creator | +| `authorityAddress` | string | Filter by authority | +| `grouping` | array | Filter by group (e.g., `[["collection", "address"]]`) | +| `delegateAddress` | string | Filter by delegate | +| `compressed` | boolean | Filter by compression status | +| `compressible` | boolean | Filter by compressibility | +| `frozen` | boolean | Filter by frozen status | +| `burnt` | boolean | Filter by burn status | +| `supply` | integer | Filter by supply amount | +| `supplyMint` | string | Filter by supply mint | +| `interface` | string | Filter by asset interface type | +| `ownerType` | string | Filter by owner type | +| `royaltyTargetType` | string | Filter by royalty target type | +| `royaltyTarget` | string | Filter by royalty recipient | +| `royaltyAmount` | integer | Filter by royalty basis points | +| `jsonUri` | string | Filter by metadata URI | +| `negate` | boolean | Invert filter logic | +| `conditionType` | string | Condition type for filters | + +**Sorting & Pagination:** + +| Parameter | Type | Description | +|-----------|------|-------------| +| `sortBy` | object | `{ sortBy: "created" \| "recent_action" \| "id" \| "none", sortDirection: "asc" \| "desc" }` | +| `limit` | integer | Max results per page | +| `page` | integer | Page number | +| `cursor` | string | Cursor for next page | +| `before` / `after` | string | Range-based pagination | +| `showFungible` | boolean | Include fungible tokens | +| `showCollectionMetadata` | boolean | Include collection metadata | + +## Pagination + +The DAS API supports three pagination modes: + +**Page-based** — simple numbered pages: +```javascript +{ page: 1, limit: 100 } +``` + +**Cursor-based** — use cursor from previous response (recommended for large datasets): +```javascript +{ cursor: 'previousResponse.result.cursor', limit: 100 } +``` + +**Range-based** — before/after asset identifiers: +```javascript +{ before: 'assetId', after: 'assetId', limit: 100 } +``` + +## Common Use Cases + +### Get all NFTs in a collection +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetsByGroup', + params: { + groupKey: 'collection', + groupValue: 'CollectionMintAddress...', + limit: 100 + } + }) +}); +``` + +### Get all compressed NFTs owned by a wallet +```javascript +const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'searchAssets', + params: { + ownerAddress: 'WalletPubkey...', + compressed: true, + limit: 100 + } + }) +}); +``` + +### Transfer a compressed NFT (get proof first) +```javascript +// 1. Get the asset proof +const proofResponse = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAssetProof', + params: { id: 'CompressedNftMint...' } + }) +}); +const { result: proof } = await proofResponse.json(); + +// 2. Get asset details +const assetResponse = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getAsset', + params: { id: 'CompressedNftMint...' } + }) +}); +const { result: asset } = await assetResponse.json(); + +// 3. Use proof.root, proof.proof, proof.node_index, and asset data +// to construct the transfer instruction via @metaplex-foundation/mpl-bubblegum +``` + +### Get all token holders for a mint +```javascript +let cursor = null; +const allAccounts = []; + +do { + const params = { mintAddress: 'TokenMint...', limit: 100 }; + if (cursor) params.cursor = cursor; + + const response = await fetch(process.env.QUICKNODE_RPC_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getTokenAccounts', + params + }) + }); + const { result } = await response.json(); + allAccounts.push(...result.token_accounts); + cursor = result.cursor; +} while (cursor); +``` + +## Documentation + +- **DAS API Overview**: https://www.quicknode.com/docs/solana/solana-das-api +- **getAsset**: https://www.quicknode.com/docs/solana/getAsset +- **getAssets**: https://www.quicknode.com/docs/solana/getAssets +- **getAssetProof**: https://www.quicknode.com/docs/solana/getAssetProof +- **getAssetProofs**: https://www.quicknode.com/docs/solana/getAssetProofs +- **getAssetsByAuthority**: https://www.quicknode.com/docs/solana/getAssetsByAuthority +- **getAssetsByCreator**: https://www.quicknode.com/docs/solana/getAssetsByCreator +- **getAssetsByGroup**: https://www.quicknode.com/docs/solana/getAssetsByGroup +- **getAssetsByOwner**: https://www.quicknode.com/docs/solana/getAssetsByOwner +- **getAssetSignatures**: https://www.quicknode.com/docs/solana/getAssetSignatures +- **getTokenAccounts**: https://www.quicknode.com/docs/solana/getTokenAccounts +- **getNftEditions**: https://www.quicknode.com/docs/solana/getNftEditions +- **searchAssets**: https://www.quicknode.com/docs/solana/searchAssets +- **Marketplace Add-on**: https://marketplace.quicknode.com/add-on/metaplex-digital-asset-standard-api diff --git a/plugins/quicknode/skills/quicknode-skill/references/sql-explorer.md b/plugins/quicknode/skills/quicknode-skill/references/sql-explorer.md new file mode 100644 index 0000000..d57a971 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/sql-explorer.md @@ -0,0 +1,2773 @@ +# Quicknode SQL Explorer Reference + +Direct SQL access to indexed blockchain data without infrastructure. Query billions of rows of on-chain data using standard SQL syntax. + +## Access Methods + +SQL Explorer can be accessed in two ways: + +### 1. Dashboard UI (Interactive) +Web-based SQL editor with syntax highlighting, schema browser, pre-built queries, and visualization tools. Access at [https://dashboard.quicknode.com/sql](https://dashboard.quicknode.com/sql) + +### 2. REST API (Programmatic) +Execute SQL queries via HTTP POST for automation and integration. This is the recommended approach for AI agents and programmatic access. + +``` +POST https://api.quicknode.com/sql/rest/v1/query +``` + +**For AI Agents:** This reference focuses on REST API usage. Use the Dashboard UI only for obtaining your API key. + +## Architecture + +``` +SQL Query → REST API → ClickHouse → Indexed Blockchain Data → JSON Response + (Auth) (Query Engine) +``` + +## Quick Start + +### Getting Your API Key + +To use SQL Explorer REST API, you need a Quicknode API key: + +1. Log in to your [Quicknode Dashboard](https://dashboard.quicknode.com) +2. Click on the **Profile icon** in the top right corner +3. Select **API Keys** from the dropdown menu +4. Either: + - Click **Create API Key** to create a new key for SQL Explorer, OR + - Use an existing API key that has SQL Explorer enabled + +**Important:** This is the same unified API key system used across all Quicknode products (RPC, Streams, IPFS, SQL Explorer, etc.). + +### Complete Working Example + +Here's a complete cURL example that works out of the box (just replace ``): + +```bash +curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: ' \ + -d '{ + "query": "SELECT timestamp, coin, side, price, size, toFloat64(price) * toFloat64(size) AS notional_usd, buyer_address, seller_address, buyer_fee, seller_fee, fee_token FROM hyperliquid_trades WHERE block_time > now() - INTERVAL 1 HOUR ORDER BY block_number DESC, trade_id DESC LIMIT 3", + "clusterId": "hyperliquid-core-mainnet" +}' +``` + +**Request Breakdown:** + +| Component | Value | Description | +|-----------|-------|-------------| +| **Endpoint** | `https://api.quicknode.com/sql/rest/v1/query` | SQL Explorer REST API endpoint | +| **Method** | `POST` | HTTP method | +| **Header** | `x-api-key: ` | Your Quicknode API key for authentication | +| **Header** | `Content-Type: application/json` | Request content type | +| **Body** | `query` | SQL query string to execute | +| **Body** | `clusterId` | Blockchain network identifier | + +**Supported Cluster IDs:** +- `hyperliquid-core-mainnet` - Hyperliquid (HyperCore) Mainnet + +**Example Response:** + +```json +{ + "data": [ + { + "timestamp": "2026-03-25 10:45:23.000", + "coin": "BTC", + "side": "B", + "price": "95432.500000000000000000", + "size": "0.150000000000000000", + "notional_usd": "14314.875000000000000000000000000000000000", + "buyer_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", + "seller_address": "0x8dF3aad3a84da6b69A4DA8aeC3eA40d9091B2Ac", + "buyer_fee": "7.156437500000000000", + "seller_fee": "7.156437500000000000", + "fee_token": "USDC" + }, + { + "timestamp": "2026-03-25 10:45:18.000", + "coin": "ETH", + "side": "A", + "price": "3245.750000000000000000", + "size": "2.500000000000000000", + "notional_usd": "8114.375000000000000000000000000000000000", + "buyer_address": "0x1a2B3c4D5e6F7a8b9C0d1E2f3A4b5C6d7E8f9A0", + "seller_address": "0x9f8e7D6c5B4a3F2e1D0c9B8a7F6e5D4c3B2a1F0", + "buyer_fee": "4.057187500000000000", + "seller_fee": "4.057187500000000000", + "fee_token": "USDC" + }, + { + "timestamp": "2026-03-25 10:45:12.000", + "coin": "SOL", + "side": "B", + "price": "142.350000000000000000", + "size": "50.000000000000000000", + "notional_usd": "7117.500000000000000000000000000000000000", + "buyer_address": "0x5a6B7c8D9e0F1a2b3C4d5E6f7A8b9C0d1E2f3A4", + "seller_address": "0x4f3E2d1C0b9A8f7E6d5C4b3A2f1E0d9C8b7A6f5", + "buyer_fee": "3.558750000000000000", + "seller_fee": "3.558750000000000000", + "fee_token": "USDC" + } + ], + "meta": [ + {"name": "timestamp", "type": "DateTime64(3, 'UTC')"}, + {"name": "coin", "type": "LowCardinality(String)"}, + {"name": "side", "type": "Enum8('B' = 1, 'A' = 2)"}, + {"name": "price", "type": "Decimal(38, 18)"}, + {"name": "size", "type": "Decimal(38, 18)"}, + {"name": "notional_usd", "type": "Decimal(38, 36)"}, + {"name": "buyer_address", "type": "FixedString(42)"}, + {"name": "seller_address", "type": "FixedString(42)"}, + {"name": "buyer_fee", "type": "Decimal(38, 18)"}, + {"name": "seller_fee", "type": "Decimal(38, 18)"}, + {"name": "fee_token", "type": "LowCardinality(String)"} + ], + "rows": 3, + "rows_before_limit_at_least": 237, + "statistics": { + "elapsed": 0.042, + "rows_read": 15847, + "bytes_read": 2456789 + } +} +``` + +**Response Fields:** + +- **`data`**: Array of result rows (each row is an object with column names as keys) +- **`meta`**: Column metadata with names and ClickHouse data types +- **`rows`**: Total number of rows returned +- **`statistics.elapsed`**: Query execution time in seconds +- **`statistics.rows_read`**: Number of rows scanned by the query +- **`statistics.bytes_read`**: Bytes of data scanned by the query + +### Testing Your Query + +**For AI Agents:** + +Execute queries directly via the REST API. Start with simple queries and add complexity as needed: + +```bash +curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \ + -H 'x-api-key: ' \ + -H 'Content-Type: application/json' \ + -d '{"query": "...", "clusterId": "hyperliquid-core-mainnet"}' +``` + +Check the `statistics` field in the response to monitor query performance (elapsed time, rows_read, bytes_read). + +## Agent Usage Guide + +**For AI Agents using this reference:** + +1. **Query Selection**: Use the "Common Use Case Mappings" table to quickly find relevant queries for user requests +2. **Parameter Substitution**: Look for `Parameters:` line under each query - replace placeholders like ``, ``, `` with actual values +3. **Keywords**: Each query lists keywords - use these for semantic search and query discovery +4. **Sort Keys**: Queries using sort key columns (marked with ⚡) run significantly faster - prioritize these when possible +5. **Time Filters**: Always add time filters for better performance and cost efficiency +6. **Sample Responses**: Check sample response format to understand data structure before processing +7. **Custom Queries**: If no pre-built query matches, use "Common Query Patterns" section as templates +8. **Authentication**: Always include `x-api-key` header - see "Getting Your API Key" section above + +## Supported Networks + +| Chain | Network | Cluster ID | Tables | Coverage | +|-------|---------|------------|--------|----------| +| Hyperliquid (HyperCore) | Mainnet | `hyperliquid-core-mainnet` | 47 | Trades, orders, fills, funding, order book diffs, perpetual markets, spot markets, blocks, transactions, system actions, builder activity, staking, ledger, clearinghouse states, oracle prices, referrals, sub-accounts, vault equities, agents, bridge events, display names, hourly metrics, daily metrics, outcome markets, perpetual positions, spot balances, quote token metadata, token prices | + +## REST API + +### Endpoint + +``` +POST https://api.quicknode.com/sql/rest/v1/query +``` + +### Authentication + +Include API key in `x-api-key` header with every request. + +### Request Format + +```json +{ + "query": "SELECT * FROM hyperliquid_trades ORDER BY block_time DESC LIMIT 10", + "clusterId": "hyperliquid-core-mainnet" +} +``` + +### Response Format + +```json +{ + "meta": [ + { + "name": "block_number", + "type": "UInt64" + }, + { + "name": "block_time", + "type": "DateTime64(6, 'UTC')" + } + ], + "data": [ + { + "block_number": 936231661, + "block_time": "2026-03-26 19:45:01.625244", + "trade_id": 496316863559640, + "coin": "cash:HOOD", + "timestamp": "2026-03-26 19:45:01.625", + "transaction_hash": "0x0fd9a48c5a19906511530437cdc2ed0205c00071f51caf37b3a24fdf191d6a4f", + "price": 70.495, + "size": 0.709, + "side": "A", + "buyer_address": "0xcee1cc9b396bde5944482f64f3e18be7b8d5df73", + "buyer_order_id": 362256178610, + "buyer_fee": -0.000149, + "buyer_closed_pnl": 0, + "buyer_start_position": 305.417, + "buyer_crossed": 0, + "buyer_dir": "Open Long", + "buyer_twap_id": null, + "buyer_builder_address": null, + "buyer_builder_fee": null, + "seller_address": "0x399965e15d4e61ec3529cc98b7f7ebb93b733336", + "seller_order_id": 362256236744, + "seller_fee": 0.001679, + "seller_closed_pnl": -0.005672, + "seller_start_position": 35.543, + "seller_crossed": 1, + "seller_dir": "Close Long", + "seller_twap_id": null, + "seller_builder_address": "0x4950994884602d1b6c6d96e4fe30f58205c39395", + "seller_builder_fee": null, + "fee_token": "USDT0", + "total_builder_fee": null, + "liquidated_user": null, + "liquidation_mark_price": null, + "liquidation_method": null, + "indexed_at": "2026-03-26 19:45:02.580" + } + ], + "rows": 10, + "rows_before_limit_at_least": 892148168, + "statistics": { + "elapsed": 1.269332554, + "rows_read": 892148586, + "bytes_read": 14274604630 + } +} +``` + +## Available Tables + +### Trading Tables + +#### hyperliquid_trades +**Sort Keys:** ⚡ block_number, trade_id | **Partition:** toYYYYMM(block_time) + +Individual trade executions on Hyperliquid. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| buyer_address | FixedString | No | Buyer Address | +| buyer_builder_address | FixedString | No | Buyer Builder Address | +| buyer_builder_fee | Decimal | No | Buyer Builder Fee | +| buyer_closed_pnl | Decimal | No | Buyer Closed Pnl | +| buyer_crossed | UInt8 | No | Buyer Crossed | +| buyer_deployer_fee | Decimal | No | Buyer Deployer Fee | +| buyer_dir | String | No | Buyer Dir | +| buyer_fee | Decimal | No | Buyer Fee | +| buyer_order_id | UInt64 | No | Buyer Order Id | +| buyer_priority_gas | Decimal | No | Buyer Priority Gas | +| buyer_start_position | Decimal | No | Buyer Start Position | +| buyer_twap_id | UInt64 | No | Buyer Twap Id | +| coin | String | No | Coin | +| fee_token | String | No | Fee Token | +| indexed_at | DateTime | No | Indexed At | +| liquidated_user | FixedString | No | Liquidated User | +| liquidation_mark_price | Decimal | No | Liquidation Mark Price | +| liquidation_method | String | No | Liquidation Method | +| price | Decimal | No | Price | +| seller_address | FixedString | No | Seller Address | +| seller_builder_address | FixedString | No | Seller Builder Address | +| seller_builder_fee | Decimal | No | Seller Builder Fee | +| seller_closed_pnl | Decimal | No | Seller Closed Pnl | +| seller_crossed | UInt8 | No | Seller Crossed | +| seller_deployer_fee | Decimal | No | Seller Deployer Fee | +| seller_dir | String | No | Seller Dir | +| seller_fee | Decimal | No | Seller Fee | +| seller_order_id | UInt64 | No | Seller Order Id | +| seller_priority_gas | Decimal | No | Seller Priority Gas | +| seller_start_position | Decimal | No | Seller Start Position | +| seller_twap_id | UInt64 | No | Seller Twap Id | +| side | Enum | No | Side | +| size | Decimal | No | Size | +| timestamp | DateTime | No | Timestamp | +| total_builder_fee | Decimal | No | Total Builder Fee | +| trade_id | UInt64 | Yes | Trade Id | +| transaction_hash | FixedString | No | Transaction Hash | +| unique_id | String | No | Unique Id | + +#### hyperliquid_fills +**Sort Keys:** ⚡ block_number, tid, user | **Partition:** toYYYYMM(block_time) + +Order fills with execution details. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| builder | FixedString | No | Builder | +| builder_fee | Decimal | No | Builder Fee | +| cloid | String | No | Cloid | +| closed_pnl | Decimal | No | Closed Pnl | +| coin | String | No | Coin | +| crossed | UInt8 | No | Crossed | +| deployer_fee | Decimal | No | Deployer Fee | +| dir | String | No | Dir | +| fee | Decimal | No | Fee | +| fee_token | String | No | Fee Token | +| hash | FixedString | No | Hash | +| indexed_at | DateTime | No | Indexed At | +| is_liquidation | UInt8 | No | Is Liquidation | +| liquidated_user | FixedString | No | Liquidated User | +| liquidation_mark_price | Decimal | No | Liquidation Mark Price | +| liquidation_method | String | No | Liquidation Method | +| oid | UInt64 | No | Oid | +| price | Decimal | No | Price | +| priority_gas | Decimal | No | Priority Gas | +| side | Enum | No | Side | +| size | Decimal | No | Size | +| start_position | Decimal | No | Start Position | +| tid | UInt64 | Yes | Tid | +| time | DateTime | No | Time | +| twap_id | UInt64 | No | Twap Id | +| user | FixedString | Yes | User | + +#### hyperliquid_orders +**Sort Keys:** ⚡ block_number, oid, status_time | **Partition:** toYYYYMM(block_time) + +Order placements, cancellations, and modifications. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| builder | String | No | Builder | +| children | String | No | Children | +| cloid | String | No | Cloid | +| coin | String | No | Coin | +| hash | FixedString | No | Hash | +| indexed_at | DateTime | No | Indexed At | +| is_position_tpsl | UInt8 | No | Is Position Tpsl | +| is_trigger | UInt8 | No | Is Trigger | +| limit_price | Decimal | No | Limit Price | +| oid | UInt64 | Yes | Oid | +| order_timestamp | DateTime | No | Order Timestamp | +| order_type | String | No | Order Type | +| orig_size | Decimal | No | Orig Size | +| reduce_only | UInt8 | No | Reduce Only | +| side | Enum | No | Side | +| size | Decimal | No | Size | +| status | String | No | Status | +| status_time | DateTime | Yes | Status Time | +| tif | String | No | Tif | +| trigger_condition | String | No | Trigger Condition | +| trigger_price | Decimal | No | Trigger Price | +| unique_id | String | No | Unique Id | +| user | FixedString | No | User | + +#### hyperliquid_order_book_diffs +**Sort Keys:** ⚡ coin, side, block_number, oid | **Partition:** toYYYYMM(block_time) + +Order book changes for reconstruction. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| coin | String | Yes | Coin | +| diff_type | String | No | Diff Type | +| indexed_at | DateTime | No | Indexed At | +| new_size | Decimal | No | New Size | +| oid | UInt64 | Yes | Oid | +| orig_size | Decimal | No | Orig Size | +| price | Decimal | No | Price | +| side | Enum | Yes | Side | +| size | Decimal | No | Size | +| user | FixedString | No | User | + +#### hyperliquid_dex_trades +**Sort Keys:** ⚡ (not specified) + +DEX trades with full buyer/seller details. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | No | Block Number | +| block_time | DateTime | No | Block Time | +| buyer_address | FixedString | No | Buyer Address | +| buyer_builder_address | FixedString | No | Buyer Builder Address | +| buyer_builder_fee | Decimal | No | Buyer Builder Fee | +| buyer_closed_pnl | Decimal | No | Buyer Closed Pnl | +| buyer_crossed | UInt8 | No | Buyer Crossed | +| buyer_dir | String | No | Buyer Dir | +| buyer_fee | Decimal | No | Buyer Fee | +| buyer_order_id | UInt64 | No | Buyer Order Id | +| buyer_start_position | Decimal | No | Buyer Start Position | +| buyer_twap_id | UInt64 | No | Buyer Twap Id | +| coin | String | No | Coin | +| fee_token | String | No | Fee Token | +| indexed_at | DateTime | No | Indexed At | +| liquidated_user | FixedString | No | Liquidated User | +| liquidation_mark_price | Decimal | No | Liquidation Mark Price | +| liquidation_method | String | No | Liquidation Method | +| market_type | String | No | Market Type | +| price | Decimal | No | Price | +| seller_address | FixedString | No | Seller Address | +| seller_builder_address | FixedString | No | Seller Builder Address | +| seller_builder_fee | Decimal | No | Seller Builder Fee | +| seller_closed_pnl | Decimal | No | Seller Closed Pnl | +| seller_crossed | UInt8 | No | Seller Crossed | +| seller_dir | String | No | Seller Dir | +| seller_fee | Decimal | No | Seller Fee | +| seller_order_id | UInt64 | No | Seller Order Id | +| seller_start_position | Decimal | No | Seller Start Position | +| seller_twap_id | UInt64 | No | Seller Twap Id | +| side | Enum | No | Side | +| size | Decimal | No | Size | +| timestamp | DateTime | No | Timestamp | +| total_builder_fee | Decimal | No | Total Builder Fee | +| trade_id | UInt64 | No | Trade Id | +| transaction_hash | FixedString | No | Transaction Hash | +| unique_id | String | No | Unique Id | +| usd_amount | Decimal | No | Usd Amount | + +#### hyperliquid_perpetual_markets +**Sort Keys:** ⚡ coin + +Perpetual market configurations. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| coin | String | Yes | Coin | +| indexed_at | DateTime | No | Indexed At | +| margin_table_id | UInt16 | No | Margin Table Id | +| market_index | UInt16 | No | Market Index | +| max_leverage | UInt16 | No | Max Leverage | +| only_isolated | UInt8 | No | Only Isolated | +| sz_decimals | UInt8 | No | Sz Decimals | + +#### hyperliquid_spot_markets +**Sort Keys:** ⚡ token_index + +Spot market configurations. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| deployer_trading_fee_share | String | No | Deployer Trading Fee Share | +| evm_contract | String | No | Evm Contract | +| full_name | String | No | Full Name | +| indexed_at | DateTime | No | Indexed At | +| is_canonical | UInt8 | No | Is Canonical | +| pair_index | UInt16 | No | Pair Index | +| pair_name | String | No | Pair Name | +| sz_decimals | UInt8 | No | Sz Decimals | +| token | String | No | Token | +| token_id | String | No | Token Id | +| token_index | UInt16 | Yes | Token Index | +| wei_decimals | UInt8 | No | Wei Decimals | + +#### hyperliquid_quote_token_meta +**Sort Keys:** ⚡ block_number, token_idx | **Partition:** toYYYYMM(snapshot_time) + +Quote token metadata and status. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| aligned_quote_info_json | String | No | Aligned Quote Info Json | +| block_number | UInt64 | Yes | Block Number | +| first_enabled_as_quote | String | No | First Enabled As Quote | +| indexed_at | DateTime | No | Indexed At | +| is_liquid_base | UInt8 | No | Is Liquid Base | +| is_liquid_quote | UInt8 | No | Is Liquid Quote | +| snapshot_time | DateTime | No | Snapshot Time | +| status | String | No | Status | +| token_idx | UInt64 | Yes | Token Idx | +| token_name | String | No | Token Name | + +#### hyperliquid_spot_pairs +**Sort Keys:** ⚡ block_number, spot_index | **Partition:** toYYYYMM(snapshot_time) + +Spot trading pair configurations. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| base_token_idx | UInt64 | No | Base Token Idx | +| base_token_name | String | No | Base Token Name | +| block_number | UInt64 | Yes | Block Number | +| deploy_time | String | No | Deploy Time | +| indexed_at | DateTime | No | Indexed At | +| pair_name | String | No | Pair Name | +| quote_token_idx | UInt64 | No | Quote Token Idx | +| quote_token_name | String | No | Quote Token Name | +| raw_spot_info_json | String | No | Raw Spot Info Json | +| snapshot_time | DateTime | No | Snapshot Time | +| spot_index | UInt64 | Yes | Spot Index | + +#### hyperliquid_token_usdc_prices +**Sort Keys:** ⚡ block_number, token_idx | **Partition:** toYYYYMM(snapshot_time) + +Token to USDC price snapshots. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| approx_usdc_px | Decimal | No | Approx Usdc Px | +| approx_usdc_px_float_1e6 | Float64 | No | Approx Usdc Px Float 1e6 | +| approx_usdc_px_raw | UInt64 | No | Approx Usdc Px Raw | +| block_number | UInt64 | Yes | Block Number | +| indexed_at | DateTime | No | Indexed At | +| raw_pair_json | String | No | Raw Pair Json | +| snapshot_time | DateTime | No | Snapshot Time | +| token_idx | UInt64 | Yes | Token Idx | +| token_name | String | No | Token Name | + +#### hyperliquid_perpetual_market_contexts +**Sort Keys:** ⚡ coin, polled_at | **Partition:** toYYYYMM(polled_at) + +Perpetual market snapshots with funding, OI, prices. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| coin | String | Yes | Coin | +| day_base_vlm | Decimal | No | Day Base Vlm | +| day_ntl_vlm | Decimal | No | Day Ntl Vlm | +| funding | Decimal | No | Funding | +| impact_ask | Decimal | No | Impact Ask | +| impact_bid | Decimal | No | Impact Bid | +| indexed_at | DateTime | No | Indexed At | +| mark_px | Decimal | No | Mark Px | +| mid_px | Decimal | No | Mid Px | +| open_interest | Decimal | No | Open Interest | +| oracle_px | Decimal | No | Oracle Px | +| polled_at | DateTime | Yes | Polled At | +| premium | Decimal | No | Premium | +| prev_day_px | Decimal | No | Prev Day Px | + +#### hyperliquid_market_volume_hourly +**Sort Keys:** ⚡ coin, hour | **Partition:** toYYYYMM(hour) + +Hourly trading volume and OHLC data. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| close | Decimal | No | Close | +| coin | String | Yes | Coin | +| high | Decimal | No | High | +| hour | DateTime | Yes | Hour | +| low | Decimal | No | Low | +| open | Decimal | No | Open | +| trade_count | UInt64 | No | Trade Count | +| volume | Decimal | No | Volume | + +#### hyperliquid_funding +**Sort Keys:** ⚡ block_number, user, coin, time | **Partition:** toYYYYMM(block_time) + +Funding payments and rates by address and coin. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| coin | String | Yes | Coin | +| funding_amount | Decimal | No | Funding Amount | +| funding_rate | Decimal | No | Funding Rate | +| hash | FixedString | No | Hash | +| indexed_at | DateTime | No | Indexed At | +| szi | Decimal | No | Szi | +| time | DateTime | Yes | Time | +| user | FixedString | Yes | User | + +#### hyperliquid_funding_summary_hourly +**Sort Keys:** ⚡ coin, hour | **Partition:** toYYYYMM(hour) + +Hourly aggregated funding rate summaries. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| avg_funding_rate | Float64 | No | Avg Funding Rate | +| coin | String | Yes | Coin | +| hour | DateTime | Yes | Hour | +| total_funding | Decimal | No | Total Funding | +| unique_users | UInt64 | No | Unique Users | + +#### hyperliquid_liquidations_hourly +**Sort Keys:** ⚡ hour, coin | **Partition:** toYYYYMM(hour) + +Hourly aggregated liquidation statistics. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| coin | String | Yes | Coin | +| hour | DateTime | Yes | Hour | +| liquidated_volume | Decimal | No | Liquidated Volume | +| liquidation_count | UInt64 | No | Liquidation Count | +| unique_liquidated_users | UInt64 | No | Unique Liquidated Users | + +#### hyperliquid_blocks +**Sort Keys:** ⚡ block_number | **Partition:** toYYYYMM(block_time) + +Hyperliquid block data. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| book_diffs_count | UInt32 | No | Book Diffs Count | +| fills_count | UInt32 | No | Fills Count | +| indexed_at | DateTime | No | Indexed At | +| misc_events_count | UInt32 | No | Misc Events Count | +| orders_count | UInt32 | No | Orders Count | +| twap_statuses_count | UInt32 | No | Twap Statuses Count | +| writer_actions_count | UInt32 | No | Writer Actions Count | + +#### hyperliquid_transactions +**Sort Keys:** ⚡ round, tx_hash, nonce | **Partition:** toYYYYMM(block_time) + +Transaction data with hashes and status. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| action | JSON | No | Action | +| action_type | String | No | Action Type | +| block_time | DateTime | No | Block Time | +| error | String | No | Error | +| indexed_at | DateTime | No | Indexed At | +| is_success | UInt8 | No | Is Success | +| nonce | UInt64 | Yes | Nonce | +| proposer | String | No | Proposer | +| round | UInt64 | Yes | Round | +| tx_hash | String | Yes | Tx Hash | +| user | String | No | User | +| vault_address | String | No | Vault Address | + +#### hyperliquid_system_actions +**Sort Keys:** ⚡ block_number, evm_tx_hash, nonce | **Partition:** toYYYYMM(block_time) + +System-level actions including HyperVM/HyperCore bridging. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| action | String | No | Action | +| action_type | String | No | Action Type | +| agent_address | FixedString | No | Agent Address | +| agent_name | String | No | Agent Name | +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| destination | FixedString | No | Destination | +| destination_dex_or_spot | UInt8 | No | Destination Dex Or Spot | +| evm_tx_hash | FixedString | Yes | Evm Tx Hash | +| from_sub_account | UInt8 | No | From Sub Account | +| hyperliquid_chain | String | No | Hyperliquid Chain | +| indexed_at | DateTime | No | Indexed At | +| is_mint | UInt8 | No | Is Mint | +| is_undelegate | UInt8 | No | Is Undelegate | +| nonce | UInt64 | Yes | Nonce | +| ntl | Decimal | No | Ntl | +| order_grouping | String | No | Order Grouping | +| signature_chain_id | String | No | Signature Chain Id | +| source_dex_or_spot | UInt8 | No | Source Dex Or Spot | +| to_perp | UInt8 | No | To Perp | +| token | UInt16 | No | Token | +| user | FixedString | No | User | +| validator | FixedString | No | Validator | +| wei | UInt256 | No | Wei | + +#### hyperliquid_builder_fills +**Sort Keys:** ⚡ builder_address, timestamp, tid | **Partition:** toYYYYMM(block_time) + +Builder-specific fill events. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | No | Block Number | +| block_time | DateTime | No | Block Time | +| builder_address | FixedString | Yes | Builder Address | +| builder_fee | Decimal | No | Builder Fee | +| cloid | String | No | Cloid | +| closed_pnl | Decimal | No | Closed Pnl | +| coin | String | No | Coin | +| created_at | DateTime | No | Created At | +| crossed | UInt8 | No | Crossed | +| dir | String | No | Dir | +| fee | Decimal | No | Fee | +| hash | FixedString | No | Hash | +| indexed_at | DateTime | No | Indexed At | +| is_liquidation | UInt8 | No | Is Liquidation | +| liquidated_user | FixedString | No | Liquidated User | +| liquidation_mark_price | Decimal | No | Liquidation Mark Price | +| liquidation_method | String | No | Liquidation Method | +| oid | UInt64 | No | Oid | +| price | Decimal | No | Price | +| side | Enum | No | Side | +| size | Decimal | No | Size | +| start_position | Decimal | No | Start Position | +| tid | UInt64 | Yes | Tid | +| timestamp | DateTime | Yes | Timestamp | +| twap_id | UInt64 | No | Twap Id | +| updated_at | DateTime | No | Updated At | +| user | FixedString | No | User | + +#### hyperliquid_builder_transactions +**Sort Keys:** ⚡ block_number, hash, user | **Partition:** toYYYYMM(block_time) + +Builder transaction activity. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| action_type | String | No | Action Type | +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| builder | String | No | Builder | +| builder_fee | Decimal | No | Builder Fee | +| coin | String | No | Coin | +| hash | String | Yes | Hash | +| indexed_at | DateTime | No | Indexed At | +| is_success | UInt8 | No | Is Success | +| user | String | Yes | User | + +#### hyperliquid_builder_labels +**Sort Keys:** ⚡ (not specified) + +Builder labels and metadata. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| builder_address | FixedString | No | Builder Address | +| builder_name | String | No | Builder Name | +| builder_category | String | No | Builder Category | +| indexed_at | DateTime | No | Indexed At | + +### Ledger Tables + +#### hyperliquid_ledger_updates +**Sort Keys:** ⚡ block_number, user, time, hash | **Partition:** toYYYYMM(block_time) + +Ledger state changes and account updates. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| account_value | Decimal | No | Account Value | +| amount | Decimal | No | Amount | +| basis | Decimal | No | Basis | +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| closing_cost | Decimal | No | Closing Cost | +| commission | Decimal | No | Commission | +| delta_type | String | No | Delta Type | +| destination | FixedString | No | Destination | +| destination_dex | String | No | Destination Dex | +| dex | String | No | Dex | +| fee | Decimal | No | Fee | +| fee_token | String | No | Fee Token | +| hash | FixedString | Yes | Hash | +| indexed_at | DateTime | No | Indexed At | +| interest_amount | Decimal | No | Interest Amount | +| is_deposit | UInt8 | No | Is Deposit | +| leverage_type | String | No | Leverage Type | +| liquidated_ntl_pos | Decimal | No | Liquidated Ntl Pos | +| liquidated_positions | String | No | Liquidated Positions | +| native_token_fee | Decimal | No | Native Token Fee | +| net_withdrawn_usd | Decimal | No | Net Withdrawn Usd | +| nonce | UInt64 | No | Nonce | +| operation | String | No | Operation | +| requested_usd | Decimal | No | Requested Usd | +| secondary_user | FixedString | No | Secondary User | +| source_dex | String | No | Source Dex | +| time | DateTime | Yes | Time | +| to_perp | UInt8 | No | To Perp | +| token | String | No | Token | +| usdc_amount | Decimal | No | Usdc Amount | +| usdc_value | Decimal | No | Usdc Value | +| user | FixedString | Yes | User | +| vault | FixedString | No | Vault | + +#### hyperliquid_asset_transfers +**Sort Keys:** ⚡ block_number, tx_hash, user | **Partition:** toYYYYMM(block_time) + +Asset transfers between addresses. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| amount | Decimal | No | Amount | +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| destination | FixedString | No | Destination | +| fee | Decimal | No | Fee | +| indexed_at | DateTime | No | Indexed At | +| nonce | UInt64 | No | Nonce | +| time | DateTime | No | Time | +| token | String | No | Token | +| transfer_type | String | No | Transfer Type | +| tx_hash | FixedString | Yes | Tx Hash | +| usdc_amount | Decimal | No | Usdc Amount | +| user | FixedString | Yes | User | + +#### hyperliquid_staking_events +**Sort Keys:** ⚡ block_number, hash | **Partition:** toYYYYMM(block_time) + +Staking, unstaking, and reward events. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| amount | Decimal | No | Amount | +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| event_type | Enum | No | Event Type | +| hash | FixedString | Yes | Hash | +| indexed_at | DateTime | No | Indexed At | +| is_finalized | UInt8 | No | Is Finalized | +| is_undelegate | UInt8 | No | Is Undelegate | +| time | DateTime | No | Time | +| user | FixedString | No | User | +| validator | FixedString | No | Validator | + +#### hyperliquid_validator_rewards +**Sort Keys:** ⚡ block_number, validator, time | **Partition:** toYYYYMM(block_time) + +Validator reward distributions. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| hash | FixedString | No | Hash | +| indexed_at | DateTime | No | Indexed At | +| reward | Decimal | No | Reward | +| time | DateTime | Yes | Time | +| validator | FixedString | Yes | Validator | + +#### hyperliquid_delegator_rewards +**Sort Keys:** ⚡ block_number, validator, delegator | **Partition:** toYYYYMM(snapshot_time) + +Delegator reward distributions. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| c | UInt64 | No | C | +| commission_bps | UInt16 | No | Commission Bps | +| delegator | FixedString | Yes | Delegator | +| e | UInt64 | No | E | +| indexed_at | DateTime | No | Indexed At | +| m | UInt64 | No | M | +| reward | Decimal | No | Reward | +| reward_wei | UInt64 | No | Reward Wei | +| snapshot_time | DateTime | No | Snapshot Time | +| source | String | No | Source | +| validator | FixedString | Yes | Validator | +| validator_total_delegated | UInt64 | No | Validator Total Delegated | + +#### hyperliquid_clearinghouse_states +**Sort Keys:** ⚡ block_number, clearinghouse, user, asset_idx | **Partition:** toYYYYMM(snapshot_time) + +Clearinghouse state snapshots. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| asset_idx | UInt16 | Yes | Asset Idx | +| block_number | UInt64 | Yes | Block Number | +| clearinghouse | UInt8 | Yes | Clearinghouse | +| coin | String | No | Coin | +| entry_notional | UInt64 | No | Entry Notional | +| funding_alltime | Int64 | No | Funding Alltime | +| funding_since_change | Int64 | No | Funding Since Change | +| funding_since_open | Int64 | No | Funding Since Open | +| indexed_at | DateTime | No | Indexed At | +| margin | UInt64 | No | Margin | +| size | Int64 | No | Size | +| snapshot_time | DateTime | No | Snapshot Time | +| usdc_balance | Int64 | No | Usdc Balance | +| user | FixedString | Yes | User | + +#### hyperliquid_perpetual_positions +**Sort Keys:** ⚡ block_number, clearinghouse, user, asset_idx | **Partition:** toYYYYMM(snapshot_time) + +Perpetual position snapshots with funding and margin data. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| asset_idx | UInt16 | Yes | Asset Idx | +| block_number | UInt64 | Yes | Block Number | +| clearinghouse | UInt8 | Yes | Clearinghouse | +| coin | String | No | Coin | +| entry_notional | UInt64 | No | Entry Notional | +| funding_alltime | Int64 | No | Funding Alltime | +| funding_since_change | Int64 | No | Funding Since Change | +| funding_since_open | Int64 | No | Funding Since Open | +| indexed_at | DateTime | No | Indexed At | +| margin | UInt64 | No | Margin | +| size | Int64 | No | Size | +| snapshot_time | DateTime | No | Snapshot Time | +| usdc_balance | Int64 | No | Usdc Balance | +| user | String | Yes | User | + +#### hyperliquid_spot_clearinghouse_states +**Sort Keys:** ⚡ block_number, user, token_idx | **Partition:** toYYYYMM(snapshot_time) + +Spot clearinghouse state snapshots. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| escrowed | Int64 | No | Escrowed | +| indexed_at | DateTime | No | Indexed At | +| snapshot_time | DateTime | No | Snapshot Time | +| token | String | No | Token | +| token_idx | UInt16 | Yes | Token Idx | +| total | Int64 | No | Total | +| user | FixedString | Yes | User | + +#### hyperliquid_spot_balances +**Sort Keys:** ⚡ block_number, user, token_idx | **Partition:** toYYYYMM(snapshot_time) + +Spot token balance snapshots. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| escrowed | Int64 | No | Escrowed | +| indexed_at | DateTime | No | Indexed At | +| snapshot_time | DateTime | No | Snapshot Time | +| token | String | No | Token | +| token_idx | UInt16 | Yes | Token Idx | +| total | Int64 | No | Total | +| user | String | Yes | User | + +#### hyperliquid_oracle_prices +**Sort Keys:** ⚡ block_number, clearinghouse, asset_idx | **Partition:** toYYYYMM(snapshot_time) + +Oracle price feeds. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| asset_idx | UInt16 | Yes | Asset Idx | +| block_number | UInt64 | Yes | Block Number | +| clearinghouse | UInt8 | Yes | Clearinghouse | +| coin | String | No | Coin | +| daily_px | String | No | Daily Px | +| indexed_at | DateTime | No | Indexed At | +| mark_px | String | No | Mark Px | +| snapshot_time | DateTime | No | Snapshot Time | + +#### hyperliquid_agents +**Sort Keys:** ⚡ block_number, agent | **Partition:** toYYYYMM(snapshot_time) + +Agent configurations. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| agent | FixedString | Yes | Agent | +| block_number | UInt64 | Yes | Block Number | +| indexed_at | DateTime | No | Indexed At | +| name | String | No | Name | +| snapshot_time | DateTime | No | Snapshot Time | +| user | FixedString | No | User | +| valid_until | String | No | Valid Until | + +#### hyperliquid_bridge +**Sort Keys:** ⚡ block_number, bridge_type, user, nonce, eth_block_number, tx_hash | **Partition:** toYYYYMM(snapshot_time) + +Bridge deposit and withdrawal events. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| amount_wei | UInt64 | No | Amount Wei | +| block_number | UInt64 | Yes | Block Number | +| bridge_type | String | Yes | Bridge Type | +| eth_block_number | UInt64 | Yes | Eth Block Number | +| event_time | String | No | Event Time | +| indexed_at | DateTime | No | Indexed At | +| nonce | UInt64 | Yes | Nonce | +| snapshot_time | DateTime | No | Snapshot Time | +| tx_hash | String | Yes | Tx Hash | +| user | FixedString | Yes | User | + +#### hyperliquid_display_names +**Sort Keys:** ⚡ block_number, user | **Partition:** toYYYYMM(snapshot_time) + +User display names and identifiers. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| display_name | String | No | Display Name | +| indexed_at | DateTime | No | Indexed At | +| snapshot_time | DateTime | No | Snapshot Time | +| user | FixedString | Yes | User | + +#### hyperliquid_register_referral +**Sort Keys:** ⚡ referral_code, user + +Referral registration events. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_time | DateTime | No | Block Time | +| error | String | No | Error | +| indexed_at | DateTime | No | Indexed At | +| is_success | UInt8 | No | Is Success | +| referral_code | String | Yes | Referral Code | +| round | UInt64 | No | Round | +| tx_hash | String | No | Tx Hash | +| user | String | Yes | User | + +#### hyperliquid_set_referrer +**Sort Keys:** ⚡ referral_code, user + +Referrer assignment events. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_time | DateTime | No | Block Time | +| error | String | No | Error | +| indexed_at | DateTime | No | Indexed At | +| is_success | UInt8 | No | Is Success | +| referral_code | String | Yes | Referral Code | +| round | UInt64 | No | Round | +| tx_hash | String | No | Tx Hash | +| user | String | Yes | User | + +#### hyperliquid_sub_accounts +**Sort Keys:** ⚡ block_number, sub_account | **Partition:** toYYYYMM(snapshot_time) + +Sub-account relationships and metadata. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| indexed_at | DateTime | No | Indexed At | +| master_account | FixedString | No | Master Account | +| name | String | No | Name | +| snapshot_time | DateTime | No | Snapshot Time | +| sub_account | FixedString | Yes | Sub Account | + +#### hyperliquid_summaries +**Sort Keys:** ⚡ user, polled_at | **Partition:** toYYYYMM(polled_at) + +Aggregated account summaries. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| cross_maintenance_margin_used | Decimal | No | Cross Maintenance Margin Used | +| cross_margin_account_value | Decimal | No | Cross Margin Account Value | +| cross_margin_total_margin_used | Decimal | No | Cross Margin Total Margin Used | +| cross_margin_total_natl_pos | Decimal | No | Cross Margin Total Natl Pos | +| cross_margin_total_raw_usd | Decimal | No | Cross Margin Total Raw Usd | +| indexed_at | DateTime | No | Indexed At | +| margin_account_value | Decimal | No | Margin Account Value | +| margin_total_margin_used | Decimal | No | Margin Total Margin Used | +| margin_total_natl_pos | Decimal | No | Margin Total Natl Pos | +| margin_total_raw_usd | Decimal | No | Margin Total Raw Usd | +| polled_at | DateTime | Yes | Polled At | +| user | String | Yes | User | +| withdrawable | Decimal | No | Withdrawable | + +#### hyperliquid_twap_statuses +**Sort Keys:** ⚡ block_number, twap_id, time | **Partition:** toYYYYMM(block_time) + +TWAP order status and execution tracking. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| block_time | DateTime | No | Block Time | +| coin | String | No | Coin | +| executed_ntl | Decimal | No | Executed Ntl | +| executed_size | Decimal | No | Executed Size | +| indexed_at | DateTime | No | Indexed At | +| minutes | UInt16 | No | Minutes | +| randomize | UInt8 | No | Randomize | +| reduce_only | UInt8 | No | Reduce Only | +| side | Enum | No | Side | +| size | Decimal | No | Size | +| status | String | No | Status | +| time | DateTime | Yes | Time | +| timestamp | DateTime | No | Timestamp | +| twap_id | UInt64 | Yes | Twap Id | +| user | FixedString | No | User | + +#### hyperliquid_vault_equities +**Sort Keys:** ⚡ block_number, vault_address, depositor | **Partition:** toYYYYMM(snapshot_time) + +Vault equity values and performance. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| depositor | FixedString | Yes | Depositor | +| indexed_at | DateTime | No | Indexed At | +| leader | FixedString | No | Leader | +| leader_commission | Float64 | No | Leader Commission | +| net_deposits | Int64 | No | Net Deposits | +| ownership_fraction | Float64 | No | Ownership Fraction | +| snapshot_time | DateTime | No | Snapshot Time | +| vault_address | FixedString | Yes | Vault Address | +| vault_name | String | No | Vault Name | + +#### hyperliquid_metrics_dex_overview +**Sort Keys:** ⚡ day, coin | **Partition:** toYYYYMM(day) + +Daily DEX metrics by coin. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| coin | String | Yes | Coin | +| day | Date | Yes | Day | +| fees | Decimal | No | Fees | +| fill_count | UInt64 | No | Fill Count | +| high_price | Decimal | No | High Price | +| liquidations | UInt64 | No | Liquidations | +| low_price | Decimal | No | Low Price | +| unique_traders | UInt64 | No | Unique Traders | +| volume_usd | Decimal | No | Volume Usd | + +#### hyperliquid_metrics_overview +**Sort Keys:** ⚡ day | **Partition:** toYYYYMM(day) + +Platform-wide aggregate metrics. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| active_traders | UInt64 | No | Active Traders | +| builder_fill_count | UInt64 | No | Builder Fill Count | +| coins_traded | UInt64 | No | Coins Traded | +| day | Date | Yes | Day | +| liquidation_count | UInt64 | No | Liquidation Count | +| liquidation_volume_usd | Decimal | No | Liquidation Volume Usd | +| total_builder_fees | Decimal | No | Total Builder Fees | +| total_fees | Decimal | No | Total Fees | +| total_fills | UInt64 | No | Total Fills | +| total_volume_usd | Decimal | No | Total Volume Usd | + + +## Pre-Built Queries + +### Quick Reference + +| # | Query Name | Category | Keywords | Description | +|---|------------|----------|----------|-------------| +| 1 | Recent Trades | Trading | trades, latest, recent | Get most recent trades across all markets | +| 2 | Volume by Coin (24h) | Trading | volume, coin, 24h, statistics | Top coins by trading volume with price stats | +| 3 | Whale Trades (24h) | Trading | whale, large, trades, notional | Large trades over $100K | +| 4 | DEX Trades (Enriched View) | Trading | dex, trades, market, type | Trades with market type classification | +| 5 | Hourly Volume | Activity | hourly, volume, timeseries | Trading volume aggregated by hour | +| 6 | Daily Volume | Activity | daily, volume, traders | Daily trading volume with unique traders | +| 7 | Top Traders by Volume (7d) | Activity | traders, volume, leaderboard | Most active traders by volume | +| 8 | Recent Fills | Fills | fills, latest, executions | Most recent order fills | +| 9 | Recent Liquidations | Fills | liquidations, risk, forced | Recent liquidation events | +| 10 | Recent Orders | Orders | orders, latest, book | Most recent orders placed | +| 11 | Order Type Distribution (1h) | Orders | orders, distribution, types | Distribution of order types and status | +| 12 | Latest Funding Payments | Funding | funding, payments, grouped | Recent funding payments grouped by coin | +| 13 | Funding Rate History by Coin | Funding | funding, rate, history, trend | Historical funding rates over time | +| 14 | Block Activity | Infrastructure | blocks, activity, transactions | Block data with transaction counts | +| 15 | Recent Transactions | Infrastructure | transactions, recent, actions | Latest transactions with action types | +| 16 | Action Type Breakdown (24h) | Infrastructure | actions, breakdown, success | Transaction success rates by action type | +| 17 | Recent Asset Transfers | Ledger | transfers, assets, movements | Latest asset transfers between addresses | +| 18 | Transfer Volume by Type (7d) | Ledger | transfers, volume, type | Asset transfer volume by type | +| 19 | Recent Ledger Updates | Ledger | ledger, updates, changes | Latest ledger state changes | +| 20 | Bridge Deposits & Withdrawals | Ledger | bridge, deposits, withdrawals | Bridge activity between chains | +| 21 | Perpetual Markets | Markets | perp, perpetual, markets, list | List all perpetual markets | +| 22 | Spot Markets | Markets | spot, markets, tokens, list | List all spot markets | +| 23 | Market Context (Latest) | Markets | market, context, funding, prices | Latest market context data | +| 24 | Oracle Prices (All DEXes) | Markets | oracle, prices, dex | Latest oracle prices for all DEXes | +| 25 | Largest Open Positions (All DEXes) | Portfolio | positions, open, largest | Biggest open positions across users | +| 26 | Spot Balances for Address | Portfolio | spot, balances, tokens, user | Spot token balances for user | +| 27 | Vault Depositor Equity | Portfolio | vault, equity, depositor | Vault holdings and performance | +| 28 | Sub-Account Mappings | Portfolio | sub, account, mappings | Sub-account relationships | +| 29 | Bot/Agent Lookups | Portfolio | bot, agent, automated | Active bots and agents | +| 30 | Display Name Lookups | Portfolio | display, name, identifier | User display names | +| 31 | Full Portfolio View | Portfolio | portfolio, full, snapshot | Complete portfolio across all assets | +| 32 | Delegator Rewards by Address | Staking | delegator, rewards, staking | Staking rewards for a delegator | +| 33 | Validator Commission History | Staking | validator, commission, earnings | Validator commission and rewards history | +| 34 | Builder Activity (24h) | Builders | builder, activity, transactions | Builder transaction activity | +| 35 | Builder Fill Volume (24h) | Builders | builder, fill, volume | Builder fill volume and fees | +| 36 | Funding Rate Summary (Hourly) | Analytics | funding, rate, summary, hourly | Hourly funding rate statistics | +| 37 | Hourly Liquidation Stats | Analytics | liquidations, hourly, stats | Hourly liquidation statistics by coin | +| 38 | Hourly OHLCV | Analytics | ohlcv, hourly, candles | Hourly OHLCV data | +| 39 | Daily Per-Coin Metrics | Analytics | daily, metrics, coin | Daily metrics by coin | +| 40 | Platform Daily Overview | Analytics | platform, overview, daily | Daily platform-wide statistics | + + +### Trading + +#### **1. Recent Trades** + +Last 100 trades with buyer/seller details. + +**Keywords:** `trades`, `latest`, `recent`, `buyer`, `seller`, `fees` + +```sql +SELECT + timestamp, + coin, + side, + price, + size, + toFloat64(price) * toFloat64(size) AS notional_usd, + buyer_address, + seller_address, + buyer_fee, + seller_fee, + fee_token +FROM hyperliquid_trades +WHERE block_time > now() - INTERVAL 1 HOUR +ORDER BY block_number DESC, trade_id DESC +LIMIT 100 +``` + +#### **2. Volume by Coin (24h)** + +Top coins by trading volume in the last 24 hours. + +**Keywords:** `volume`, `coin`, `24h`, `statistics`, `high`, `low`, `avg` + +```sql +SELECT + coin, + count() AS trade_count, + sum(toFloat64(price) * toFloat64(size)) AS volume_usd, + min(toFloat64(price)) AS low, + max(toFloat64(price)) AS high, + avg(toFloat64(price)) AS avg_price +FROM hyperliquid_trades +WHERE timestamp > now() - INTERVAL 24 HOUR +GROUP BY coin +ORDER BY volume_usd DESC +LIMIT 50 +``` + +#### **3. Whale Trades (24h)** + +Trades over $100K in the last 24 hours. + +**Keywords:** `whale`, `large`, `trades`, `notional`, `high-value` + +```sql +SELECT + timestamp, + coin, + side, + price, + size, + toFloat64(price) * toFloat64(size) AS notional_usd, + buyer_address, + seller_address +FROM hyperliquid_trades +WHERE block_time > now() - INTERVAL 24 HOUR + AND toFloat64(price) * toFloat64(size) > 100000 +ORDER BY notional_usd DESC +LIMIT 100 +``` + +#### **4. DEX Trades (Enriched View)** + +Trades with market type (perpetual vs spot) from the enriched view. + +**Keywords:** `dex`, `trades`, `market`, `type`, `perpetual`, `spot` + +```sql +SELECT + timestamp, + coin, + market_type, + side, + price, + size, + usd_amount, + buyer_address, + seller_address +FROM hyperliquid_dex_trades +WHERE block_time > now() - INTERVAL 1 HOUR +ORDER BY block_number DESC, trade_id DESC +LIMIT 100 +``` + +--- + +### Activity + +#### **5. Hourly Volume** + +Trading volume aggregated by hour for the last 7 days. + +**Keywords:** `hourly`, `volume`, `timeseries`, `trends` + +```sql +SELECT + toStartOfHour(timestamp) AS hour, + count() AS trades, + sum(toFloat64(price) * toFloat64(size)) AS volume_usd +FROM hyperliquid_trades +WHERE timestamp > now() - INTERVAL 7 DAY +GROUP BY hour +ORDER BY hour DESC +``` + +#### **6. Daily Volume** + +Daily trading volume for the last 30 days. + +**Keywords:** `daily`, `volume`, `traders`, `unique`, `users` + +```sql +SELECT + toStartOfDay(timestamp) AS day, + count() AS trades, + sum(toFloat64(price) * toFloat64(size)) AS volume_usd, + uniqExact(arrayJoin([buyer_address, seller_address])) AS unique_traders +FROM hyperliquid_trades +WHERE timestamp > now() - INTERVAL 30 DAY +GROUP BY day +ORDER BY day DESC +``` + +#### **7. Top Traders by Volume (7d)** + +Most active traders by volume in the last 7 days. + +**Keywords:** `traders`, `volume`, `leaderboard`, `top`, `active` + +```sql +SELECT + trader, + count() AS trade_count, + sum(volume) AS total_volume, + uniqExact(coin) AS coins_traded +FROM ( + SELECT + buyer_address AS trader, + toFloat64(price) * toFloat64(size) AS volume, + coin + FROM hyperliquid_trades + WHERE timestamp > now() - INTERVAL 7 DAY + UNION ALL + SELECT + seller_address AS trader, + toFloat64(price) * toFloat64(size) AS volume, + coin + FROM hyperliquid_trades + WHERE timestamp > now() - INTERVAL 7 DAY +) +GROUP BY trader +ORDER BY total_volume DESC +LIMIT 50 +``` + +--- + +### Fills + +#### **8. Recent Fills** + +Most recent order fills with fee and PnL information. + +**Keywords:** `fills`, `latest`, `executions`, `pnl`, `fees` + +```sql +SELECT + time, + coin, + side, + price, + size, + fee, + fee_token, + closed_pnl, + dir, + user, + hash +FROM hyperliquid_fills +WHERE block_time > now() - INTERVAL 1 HOUR +ORDER BY block_number DESC, tid DESC +LIMIT 100 +``` + +#### **9. Recent Liquidations** + +Recent liquidation events with liquidated user details. + +**Keywords:** `liquidations`, `risk`, `forced`, `closures`, `mark-price` + +```sql +SELECT + time, + coin, + side, + price, + size, + toFloat64(price) * toFloat64(size) AS notional, + liquidated_user, + liquidation_mark_price, + liquidation_method, + user +FROM hyperliquid_fills +WHERE block_time > now() - INTERVAL 24 HOUR + AND is_liquidation = 1 +ORDER BY block_number DESC, tid DESC +LIMIT 100 +``` + +--- + +### Orders + +#### **10. Recent Orders** + +Most recent orders placed on the platform. + +**Keywords:** `orders`, `latest`, `book`, `limit`, `market` + +```sql +SELECT + status_time, + coin, + side, + order_type, + limit_price, + size, + orig_size, + status, + tif, + user +FROM hyperliquid_orders +WHERE block_time > now() - INTERVAL 1 HOUR +ORDER BY block_number DESC, oid DESC +LIMIT 100 +``` + +#### **11. Order Type Distribution (1h)** + +Distribution of order types and status in the last hour. + +**Keywords:** `orders`, `distribution`, `types`, `status`, `statistics` + +```sql +SELECT + order_type, + side, + status, + count() AS order_count, + uniqExact(user) AS unique_users +FROM hyperliquid_orders +WHERE block_time > now() - INTERVAL 1 HOUR +GROUP BY order_type, side, status +ORDER BY order_count DESC +``` + +--- + +### Funding + +#### **12. Latest Funding Payments** + +Recent funding rate payments grouped by coin and rate. + +**Keywords:** `funding`, `payments`, `rates`, `position`, `fees` + +```sql +SELECT + coin, + funding_rate, + count() AS payments, + sum(toFloat64(funding_amount)) AS total_funding, + avg(toFloat64(szi)) AS avg_position_size +FROM hyperliquid_funding +WHERE time > now() - INTERVAL 8 HOUR +GROUP BY coin, funding_rate +ORDER BY abs(total_funding) DESC +LIMIT 50 +``` + +#### **13. Funding Rate History by Coin** + +Historical funding rates over time for specific coins. + +**Keywords:** `funding`, `rate`, `history`, `trend`, `timeseries` + +```sql +SELECT + toStartOfHour(time) AS hour, + coin, + avg(toFloat64(funding_rate)) AS avg_funding_rate, + count() AS payment_count, + sum(toFloat64(funding_amount)) AS total_funding +FROM hyperliquid_funding +WHERE time > now() - INTERVAL 7 DAY + AND coin IN ('BTC', 'ETH', 'SOL') +GROUP BY hour, coin +ORDER BY hour DESC, coin +LIMIT 500 +``` + +--- + +### Infrastructure + +#### **14. Block Activity** + +Block data with transaction and event counts. + +**Keywords:** `blocks`, `activity`, `transactions`, `events`, `fills` + +```sql +SELECT + block_number, + block_time, + fills_count, + orders_count, + misc_events_count, + book_diffs_count, + twap_statuses_count, + writer_actions_count +FROM hyperliquid_blocks +ORDER BY block_number DESC +LIMIT 100 +``` + +#### **15. Recent Transactions** + +Latest transactions with action types and success status. + +**Keywords:** `transactions`, `recent`, `actions`, `success`, `errors` + +```sql +SELECT + block_time, + round, + tx_hash, + user, + action_type, + is_success, + error +FROM hyperliquid_transactions +ORDER BY round DESC +LIMIT 100 +``` + +#### **16. Action Type Breakdown (24h)** + +Transaction success rates by action type in the last 24 hours. + +**Keywords:** `actions`, `breakdown`, `success`, `rates`, `statistics` + +```sql +SELECT + action_type, + count() AS tx_count, + countIf(is_success = 1) AS success_count, + countIf(is_success = 0) AS failed_count, + round(countIf(is_success = 1) / count() * 100, 2) AS success_rate +FROM hyperliquid_transactions +WHERE block_time > now() - INTERVAL 24 HOUR +GROUP BY action_type +ORDER BY tx_count DESC +``` + +--- + +### Ledger + +#### **17. Recent Asset Transfers** + +Latest asset transfers between addresses. + +**Keywords:** `transfers`, `assets`, `movements`, `deposits`, `withdrawals` + +```sql +SELECT + time, + transfer_type, + user, + destination, + token, + amount, + usdc_amount, + fee, + tx_hash +FROM hyperliquid_asset_transfers +ORDER BY block_number DESC +LIMIT 100 +``` + +#### **18. Transfer Volume by Type (7d)** + +Asset transfer volume by type in the last 7 days. + +**Keywords:** `transfers`, `volume`, `type`, `aggregated`, `statistics` + +```sql +SELECT + transfer_type, + count() AS transfer_count, + sum(toFloat64(usdc_amount)) AS total_usdc, + uniqExact(user) AS unique_users +FROM hyperliquid_asset_transfers +WHERE time > now() - INTERVAL 7 DAY +GROUP BY transfer_type +ORDER BY total_usdc DESC +``` + +#### **19. Recent Ledger Updates** + +Latest ledger state changes including balance updates. + +**Keywords:** `ledger`, `updates`, `changes`, `balances`, `deltas` + +```sql +SELECT + time, + delta_type, + user, + usdc_amount, + token, + amount, + destination, + fee, + hash +FROM hyperliquid_ledger_updates +ORDER BY block_number DESC +LIMIT 100 +``` + +#### **20. Bridge Deposits & Withdrawals** + +Bridge activity between chains at latest snapshot. + +**Keywords:** `bridge`, `deposits`, `withdrawals`, `cross-chain`, `eth` + +```sql +SELECT + bridge_type, + user, + amount_wei, + tx_hash, + eth_block_number, + event_time, + snapshot_time +FROM hyperliquid_bridge +WHERE block_number = (SELECT max(block_number) FROM hyperliquid_bridge) +ORDER BY toFloat64(amount_wei) DESC +LIMIT 100 +``` + +--- + +### Markets + +#### **21. Perpetual Markets** + +List all perpetual markets with leverage and decimals. + +**Keywords:** `perp`, `perpetual`, `markets`, `list`, `leverage` + +```sql +SELECT + coin, + market_index, + sz_decimals, + max_leverage, + only_isolated +FROM hyperliquid_perpetual_markets +ORDER BY coin +LIMIT 500 +``` + +#### **22. Spot Markets** + +List all spot markets with token details. + +**Keywords:** `spot`, `markets`, `tokens`, `list`, `canonical` + +```sql +SELECT + token_index, + token, + token_id, + full_name, + sz_decimals, + wei_decimals, + is_canonical, + evm_contract +FROM hyperliquid_spot_markets +ORDER BY token_index +LIMIT 500 +``` + +#### **23. Market Context (Latest)** + +Latest market context data including funding, OI, and prices. + +**Keywords:** `market`, `context`, `funding`, `prices`, `open-interest` + +```sql +SELECT + coin, + funding, + open_interest, + mark_px, + oracle_px, + mid_px, + premium, + day_ntl_vlm, + prev_day_px +FROM hyperliquid_perpetual_market_contexts +WHERE polled_at > now() - INTERVAL 5 MINUTE +ORDER BY toFloat64(day_ntl_vlm) DESC +LIMIT 100 +``` + +#### **24. Oracle Prices (All DEXes)** + +Mark and daily prices for all assets at the latest snapshot. + +**Keywords:** `oracle`, `prices`, `dex`, `mark`, `daily` + +```sql +SELECT + clearinghouse, + coin, + mark_px, + daily_px, + snapshot_time +FROM hyperliquid_oracle_prices +WHERE block_number = (SELECT max(block_number) FROM hyperliquid_oracle_prices) +ORDER BY clearinghouse, asset_idx +``` + +### Outcome Markets + +#### hyperliquid_outcome_markets +**Sort Keys:** ⚡ (not specified) + +View combining outcome market data with sides and metadata. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| base_token_idx | UInt64 | No | Base Token Idx | +| block_number | UInt64 | No | Block Number | +| coin | String | No | Coin | +| description | String | No | Description | +| expiry | String | No | Expiry | +| fee_share | Float64 | No | Fee Share | +| first_enabled_as_quote | String | No | First Enabled As Quote | +| indexed_at | DateTime | No | Indexed At | +| is_liquid_base | UInt8 | No | Is Liquid Base | +| is_liquid_quote | UInt8 | No | Is Liquid Quote | +| locked | UInt8 | No | Locked | +| name | String | No | Name | +| ndo | UInt64 | No | Ndo | +| nou | UInt64 | No | Nou | +| nqu | UInt64 | No | Nqu | +| outcome_class | String | No | Outcome Class | +| outcome_id | UInt64 | No | Outcome Id | +| pair_name | String | No | Pair Name | +| period | String | No | Period | +| quote_raw | UInt64 | No | Quote Raw | +| quote_token_idx | UInt64 | No | Quote Token Idx | +| quote_token_name | String | No | Quote Token Name | +| quote_token_status | String | No | Quote Token Status | +| quote_token_usdc_px | Decimal | No | Quote Token Usdc Px | +| quote_token_usdc_px_raw | UInt64 | No | Quote Token Usdc Px Raw | +| raw_side_json | String | No | Raw Side Json | +| raw_spec_json | String | No | Raw Spec Json | +| side_count | UInt16 | No | Side Count | +| side_index | UInt8 | No | Side Index | +| side_name | String | No | Side Name | +| snapshot_time | DateTime | No | Snapshot Time | +| spot_deploy_time | String | No | Spot Deploy Time | +| spot_index | UInt64 | No | Spot Index | +| spot_resolution | String | No | Spot Resolution | +| target_price | String | No | Target Price | +| token_idx | UInt64 | No | Token Idx | +| token_name | String | No | Token Name | +| underlying | String | No | Underlying | + +#### hyperliquid_outcome_meta +**Sort Keys:** ⚡ block_number, outcome_id | **Partition:** toYYYYMM(snapshot_time) + +Outcome market metadata and configurations. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| deploy_gas_auction_json | String | No | Deploy Gas Auction Json | +| description | String | No | Description | +| expiry | String | No | Expiry | +| fee_share | Float64 | No | Fee Share | +| indexed_at | DateTime | No | Indexed At | +| locked | UInt8 | No | Locked | +| name | String | No | Name | +| ndo | UInt64 | No | Ndo | +| nou | UInt64 | No | Nou | +| nqu | UInt64 | No | Nqu | +| outcome_class | String | No | Outcome Class | +| outcome_id | UInt64 | Yes | Outcome Id | +| period | String | No | Period | +| quote_raw | UInt64 | No | Quote Raw | +| raw_spec_json | String | No | Raw Spec Json | +| side_count | UInt16 | No | Side Count | +| snapshot_time | DateTime | No | Snapshot Time | +| target_price | String | No | Target Price | +| underlying | String | No | Underlying | + +#### hyperliquid_outcome_question_members +**Sort Keys:** ⚡ block_number, question_id, outcome_id | **Partition:** toYYYYMM(snapshot_time) + +Mapping between questions and outcome markets. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| indexed_at | DateTime | No | Indexed At | +| is_fallback | UInt8 | No | Is Fallback | +| is_settled | UInt8 | No | Is Settled | +| outcome_id | UInt64 | Yes | Outcome Id | +| question_id | UInt64 | Yes | Question Id | +| snapshot_time | DateTime | No | Snapshot Time | +| source | String | No | Source | + +#### hyperliquid_outcome_questions +**Sort Keys:** ⚡ block_number, question_id | **Partition:** toYYYYMM(snapshot_time) + +Outcome market questions and settlement data. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| block_number | UInt64 | Yes | Block Number | +| description | String | No | Description | +| fallback_outcome | UInt64 | No | Fallback Outcome | +| indexed_at | DateTime | No | Indexed At | +| name | String | No | Name | +| named_outcomes_json | String | No | Named Outcomes Json | +| question_id | UInt64 | Yes | Question Id | +| raw_question_json | String | No | Raw Question Json | +| settled_named_outcomes_json | String | No | Settled Named Outcomes Json | +| snapshot_time | DateTime | No | Snapshot Time | + +#### hyperliquid_outcome_sides +**Sort Keys:** ⚡ block_number, outcome_id, side_index | **Partition:** toYYYYMM(snapshot_time) + +Outcome market sides and token configurations. + +| Column | Type | Sort Key | Description | +|--------|------|----------|-------------| +| base_token_idx | UInt64 | No | Base Token Idx | +| block_number | UInt64 | Yes | Block Number | +| coin | String | No | Coin | +| indexed_at | DateTime | No | Indexed At | +| outcome_id | UInt64 | Yes | Outcome Id | +| quote_token_idx | UInt64 | No | Quote Token Idx | +| quote_token_name | String | No | Quote Token Name | +| raw_side_json | String | No | Raw Side Json | +| side_index | UInt8 | Yes | Side Index | +| side_m_raw | String | No | Side M Raw | +| side_m_value | Int64 | No | Side M Value | +| side_name | String | No | Side Name | +| snapshot_time | DateTime | No | Snapshot Time | +| spot_index | UInt64 | No | Spot Index | +| spot_indices_json | String | No | Spot Indices Json | +| spot_resolution | String | No | Spot Resolution | +| token_idx | UInt64 | No | Token Idx | +| token_name | String | No | Token Name | + +### Portfolio & Positions + +#### **25. Largest Open Positions (All DEXes)** + +Top positions by entry notional across all clearinghouses. + +**Keywords:** `positions`, `open`, `largest`, `notional`, `clearinghouse` + +```sql +SELECT + user, + clearinghouse, + coin, + size, + entry_notional, + margin, + funding_alltime, + snapshot_time +FROM hyperliquid_clearinghouse_states +WHERE block_number = (SELECT max(block_number) FROM hyperliquid_clearinghouse_states) + AND toFloat64(size) != 0 +ORDER BY abs(toFloat64(entry_notional)) DESC +LIMIT 100 +``` + +#### **26. Spot Balances for Address** + +Spot token balances for a specific user address. + +**Keywords:** `spot`, `balances`, `tokens`, `user`, `holdings` + +```sql +-- Replace address below +SELECT + token, + token_idx, + total, + escrowed, + snapshot_time +FROM hyperliquid_spot_clearinghouse_states +WHERE user = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_spot_clearinghouse_states) + AND toFloat64(total) != 0 +ORDER BY abs(toFloat64(total)) DESC +``` + +#### **27. Vault Depositor Equity** + +Vault holdings and performance for depositors. + +**Keywords:** `vault`, `equity`, `depositor`, `ownership`, `commission` + +```sql +SELECT + vault_name, + depositor, + ownership_fraction, + net_deposits, + leader, + leader_commission, + snapshot_time +FROM hyperliquid_vault_equities +WHERE block_number = (SELECT max(block_number) FROM hyperliquid_vault_equities) +ORDER BY toFloat64(ownership_fraction) DESC +LIMIT 100 +``` + +#### **28. Sub-Account Mappings** + +Sub-account relationships for a specific master account. + +**Keywords:** `sub`, `account`, `mappings`, `master`, `relationships` + +```sql +-- Replace address to find sub-accounts for a specific master +SELECT + sub_account, + master_account, + name, + snapshot_time +FROM hyperliquid_sub_accounts +WHERE master_account = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_sub_accounts) +ORDER BY name +``` + +#### **29. Bot/Agent Lookups** + +Active bots and agents for a specific user. + +**Keywords:** `bot`, `agent`, `automated`, `trading`, `permissions` + +```sql +-- Replace address to find agents for a specific user +SELECT + agent, + user, + name, + valid_until, + snapshot_time +FROM hyperliquid_agents +WHERE user = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_agents) +ORDER BY name +``` + +#### **30. Display Name Lookups** + +User display names for address resolution. + +**Keywords:** `display`, `name`, `identifier`, `username`, `alias` + +```sql +SELECT + user, + display_name, + snapshot_time +FROM hyperliquid_display_names +WHERE block_number = (SELECT max(block_number) FROM hyperliquid_display_names) +ORDER BY display_name +LIMIT 100 +``` + +#### **31. Full Portfolio View** + +Complete portfolio across all assets for a specific address. + +**Keywords:** `portfolio`, `full`, `snapshot`, `complete`, `holdings` + +```sql +-- Replace address below for full portfolio snapshot +SELECT + 'perps' AS type, + coin AS asset, + toFloat64(size) AS amount, + toFloat64(entry_notional) AS extra +FROM hyperliquid_clearinghouse_states +WHERE user = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_clearinghouse_states) + AND toFloat64(size) != 0 +UNION ALL +SELECT + 'spot', + token, + toFloat64(total), + toFloat64(escrowed) +FROM hyperliquid_spot_clearinghouse_states +WHERE user = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_spot_clearinghouse_states) + AND toFloat64(total) != 0 +UNION ALL +SELECT + 'vault', + vault_name, + toFloat64(ownership_fraction) * 1000000, + toFloat64(net_deposits) +FROM hyperliquid_vault_equities +WHERE depositor = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_vault_equities) +UNION ALL +SELECT + 'delegation', + validator, + toFloat64(reward), + toFloat64(commission_bps) +FROM hyperliquid_delegator_rewards +WHERE delegator = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_delegator_rewards) +``` + +### Staking & Rewards + +#### **32. Delegator Rewards by Address** + +Staking rewards for a specific delegator address. + +**Keywords:** `delegator`, `rewards`, `staking`, `delegation`, `validator` + +```sql +-- Replace address below +SELECT + delegator, + validator, + reward, + commission_bps, + snapshot_time, + block_number +FROM hyperliquid_delegator_rewards +WHERE delegator = lower('0xYOUR_ADDRESS_HERE') + AND block_number = (SELECT max(block_number) FROM hyperliquid_delegator_rewards) +ORDER BY toFloat64(reward) DESC +``` + +#### **33. Validator Commission History** + +Validator commission and rewards history over time. + +**Keywords:** `validator`, `commission`, `earnings`, `rewards`, `history` + +```sql +SELECT + block_number, + snapshot_time, + commission_bps, + count() AS delegator_count, + sum(reward) AS total_pending_rewards +FROM hyperliquid_delegator_rewards +WHERE validator = lower('0xYOUR_VALIDATOR_HERE') +GROUP BY block_number, snapshot_time, commission_bps +ORDER BY block_number DESC +LIMIT 50 +``` + +### Builders + +#### **34. Builder Activity (24h)** + +Builder transaction activity in the last 24 hours. + +**Keywords:** `builder`, `activity`, `transactions`, `fees`, `users` + +```sql +SELECT + b.builder, + l.builder_name, + count() AS tx_count, + sum(toFloat64(b.builder_fee)) AS total_fees, + uniqExact(b.user) AS unique_users +FROM hyperliquid_builder_transactions b +LEFT JOIN hyperliquid_builder_labels l ON b.builder = l.builder_address +WHERE b.block_time > now() - INTERVAL 24 HOUR +GROUP BY b.builder, l.builder_name +ORDER BY total_fees DESC +LIMIT 50 +``` + +#### **35. Builder Fill Volume (24h)** + +Builder fill volume and fees in the last 24 hours. + +**Keywords:** `builder`, `fill`, `volume`, `fees`, `statistics` + +```sql +SELECT + builder_address, + count() AS fills, + sum(toFloat64(price) * toFloat64(size)) AS volume_usd, + sum(toFloat64(builder_fee)) AS total_builder_fees, + uniqExact(user) AS unique_users +FROM hyperliquid_builder_fills +WHERE block_time > now() - INTERVAL 24 HOUR +GROUP BY builder_address +ORDER BY volume_usd DESC +LIMIT 50 +``` + +### Analytics + +#### **36. Funding Rate Summary (Hourly)** + +Hourly funding rate statistics from aggregated view. + +**Keywords:** `funding`, `rate`, `summary`, `hourly`, `aggregated` + +```sql +SELECT + coin, + hour, + avg_funding_rate, + total_funding, + unique_users +FROM hyperliquid_funding_summary_hourly +WHERE hour > now() - INTERVAL 24 HOUR +ORDER BY hour DESC, abs(toFloat64(avg_funding_rate)) DESC +LIMIT 100 +``` + +#### **37. Hourly Liquidation Stats** + +Hourly liquidation statistics by coin. + +**Keywords:** `liquidations`, `hourly`, `stats`, `volume`, `users` + +```sql +SELECT + hour, + coin, + liquidation_count, + liquidated_volume, + unique_liquidated_users +FROM hyperliquid_liquidations_hourly +ORDER BY hour DESC, toFloat64(liquidated_volume) DESC +LIMIT 100 +``` + +#### **38. Hourly OHLCV** + +Hourly OHLCV data for a specific coin. + +**Keywords:** `ohlcv`, `hourly`, `candles`, `price`, `volume` + +```sql +SELECT + coin, + hour, + volume, + trade_count, + high, + low, + open, + close +FROM hyperliquid_market_volume_hourly +WHERE hour > now() - INTERVAL 24 HOUR + AND coin = 'BTC' +ORDER BY hour DESC +``` + +#### **39. Daily Per-Coin Metrics** + +Daily metrics by coin from aggregated view. + +**Keywords:** `daily`, `metrics`, `coin`, `volume`, `fees`, `liquidations` + +```sql +SELECT + day, + coin, + volume_usd, + fill_count, + unique_traders, + fees, + liquidations, + high_price, + low_price +FROM hyperliquid_metrics_dex_overview +ORDER BY day DESC, volume_usd DESC +LIMIT 100 +``` + +#### **40. Platform Daily Overview** + +Daily platform-wide statistics and metrics. + +**Keywords:** `platform`, `overview`, `daily`, `total`, `aggregate` + +```sql +SELECT + day, + total_volume_usd, + total_fills, + active_traders, + total_fees, + liquidation_count, + liquidation_volume_usd, + coins_traded, + total_builder_fees, + builder_fill_count +FROM hyperliquid_metrics_overview +ORDER BY day DESC +LIMIT 30 +``` + +## Common Query Patterns + +### Time-Based Analysis + +```sql +-- Trading volume over time +SELECT + toStartOfHour(time) AS hour, + coin, + SUM(toFloat64(size)) AS volume, + COUNT(*) AS trade_count, + AVG(toFloat64(price)) AS avg_price +FROM hyperliquid_trades +WHERE time >= now() - INTERVAL 7 DAY +GROUP BY hour, coin +ORDER BY hour DESC, volume DESC +``` + +### User Analytics + +```sql +-- User position and PnL summary +SELECT + user, + coin, + SUM(toFloat64(size) * CASE WHEN side = 'buy' THEN 1 ELSE -1 END) AS net_position, + SUM(toFloat64(closed_pnl)) AS realized_pnl, + SUM(toFloat64(fee)) AS total_fees +FROM hyperliquid_trades +WHERE time >= now() - INTERVAL 30 DAY +GROUP BY user, coin +HAVING abs(net_position) > 0 +ORDER BY realized_pnl DESC +``` + +### Market Depth Analysis + +```sql +-- Order book snapshot +SELECT + side, + price AS price, + SUM(toFloat64(size)) AS total_size, + COUNT(*) AS order_count +FROM hyperliquid_order_book_diffs +WHERE coin = 'BTC' + AND time >= now() - INTERVAL 1 MINUTE +GROUP BY side, price +ORDER BY side DESC, price DESC +``` + +### Funding Rate Analysis + +```sql +-- Funding rate trends +SELECT + coin, + toStartOfDay(time) AS day, + AVG(toFloat64(funding_rate)) AS avg_funding_rate, + SUM(toFloat64(usdc)) AS total_funding_paid +FROM hyperliquid_funding +WHERE time >= now() - INTERVAL 30 DAY +GROUP BY coin, day +ORDER BY day DESC, coin +``` + +### Builder Analytics + +```sql +-- Builder performance comparison +SELECT + builder_address, + toStartOfDay(time) AS day, + COUNT(*) AS fill_count, + SUM(toFloat64(size)) AS volume, + SUM(toFloat64(builder_fee)) AS fees_earned, + COUNT(DISTINCT user) AS unique_traders +FROM hyperliquid_builder_fills +WHERE time >= now() - INTERVAL 7 DAY +GROUP BY builder_address, day +ORDER BY day DESC, fees_earned DESC +``` + +### Whale Tracking + +```sql +-- Large trades monitoring +SELECT + toDateTime(time) AS time, + coin, + side, + price AS price, + size AS size, + toFloat64(price) * toFloat64(size) AS notional, + user +FROM hyperliquid_trades +WHERE toFloat64(price) * toFloat64(size) >= 500000 + AND time >= now() - INTERVAL 24 HOUR +ORDER BY notional DESC +``` + +### Liquidation Monitoring + +```sql +-- Liquidation risk analysis by coin +SELECT + hour, + coin, + liquidation_count, + liquidated_volume, + unique_liquidated_users +FROM hyperliquid_liquidations_hourly +WHERE hour >= now() - INTERVAL 7 DAY +ORDER BY hour DESC, liquidated_volume DESC +LIMIT 100 +``` + +## SQL Syntax Support + +SQL Explorer supports standard SQL with ClickHouse extensions: + +### Functions + +- **Date/Time**: `toDateTime()`, `toStartOfHour()`, `toStartOfDay()`, `now()`, `INTERVAL` +- **Encoding**: `base58Encode()`, `substring()` +- **Aggregations**: `SUM()`, `AVG()`, `COUNT()`, `MAX()`, `MIN()` +- **Conditionals**: `CASE WHEN`, `IF()`, `countIf()` +- **Math**: `round()`, `abs()`, `toFloat64()` +- **String**: `lower()`, `upper()`, `concat()` + +### Clauses + +- `SELECT`, `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT` +- `JOIN` (INNER, LEFT, RIGHT, FULL) +- `WITH` (CTEs/Common Table Expressions) +- Subqueries +- Window functions + +### Operators + +- Comparison: `=`, `!=`, `<`, `>`, `<=`, `>=` +- Logical: `AND`, `OR`, `NOT` +- Arithmetic: `+`, `-`, `*`, `/` +- String: `LIKE`, `IN`, `NOT IN` +- NULL handling: `IS NULL`, `IS NOT NULL` + +### Common Patterns for Agents + +**Address Handling:** +```sql +-- Addresses are case-sensitive FixedString(42) +WHERE user = '0x1234567890abcdef1234567890abcdef12345678' -- Use exact case + +-- Case-insensitive search (slower) +WHERE lower(user) = lower('0x1234...') +``` + +**Time Conversions:** +```sql +-- Convert timestamps to readable format +SELECT toDateTime(block_time) AS readable_time + +-- Time bucketing for aggregations +SELECT toStartOfHour(time) AS hour_bucket +SELECT toStartOfDay(time) AS day_bucket + +-- Recent data (last 24 hours, 7 days, 30 days) +WHERE block_time >= now() - INTERVAL 24 HOUR +WHERE time >= now() - INTERVAL 7 DAY +``` + +**String to Number Conversions:** +```sql +-- Most numeric fields stored as String - convert for math +SELECT toFloat64(price) AS price_numeric +SELECT toFloat64(size) AS size_numeric + +-- Calculations require conversion +WHERE toFloat64(price) * toFloat64(size) > 100000 +SELECT SUM(toFloat64(fee)) AS total_fees +``` + +**NULL Handling:** +```sql +-- Builder fields can be NULL +WHERE builder_address IS NOT NULL +SELECT Nullable(FixedString(42)) AS nullable_field +``` + +**Aggregation Patterns:** +```sql +-- Count distinct users +SELECT COUNT(DISTINCT user) AS unique_users + +-- Sum with conversion +SELECT SUM(toFloat64(size)) AS total_volume + +-- Average with filtering +SELECT AVG(toFloat64(price)) AS avg_price +``` + +## Query Optimization + +### Use Sort Keys + +Queries filtering on sort key columns run significantly faster: + +```sql +-- Fast - filters on sort key +SELECT * FROM hyperliquid_trades +WHERE user = '0xADDRESS' +ORDER BY block_number DESC + +-- Slower - no sort key filter +SELECT * FROM hyperliquid_trades +WHERE coin = 'BTC' +``` + +### Leverage Time Partitions + +Filter by time to reduce data scanned: + +```sql +-- Good - scans only recent partition +SELECT * FROM hyperliquid_trades +WHERE block_time >= now() - INTERVAL 1 DAY + +-- Avoid - scans all partitions +SELECT * FROM hyperliquid_trades +WHERE trade_id > 1000000 +``` + +### Limit Result Sets + +Always use `LIMIT` for exploratory queries: + +```sql +SELECT * FROM hyperliquid_trades +ORDER BY block_time DESC +LIMIT 1000 +``` + +### Aggregate Early + +Use CTEs to aggregate before joining: + +```sql +WITH user_volumes AS ( + SELECT user, SUM(toFloat64(size)) AS volume + FROM hyperliquid_trades + WHERE block_time >= now() - INTERVAL 1 DAY + GROUP BY user +) +SELECT * FROM user_volumes +WHERE volume > 10000 +ORDER BY volume DESC +``` + +### Use Appropriate Data Types + +Convert strings to numbers only when needed: + +```sql +-- Efficient +SELECT user, size FROM hyperliquid_trades WHERE user = '0xADDRESS' + +-- Less efficient +SELECT user, toFloat64(size) FROM hyperliquid_trades WHERE toFloat64(size) > 100 +``` + +## Best Practices + +1. **Start with time filters** - Reduce data scanned using `block_time` or `time` filters +2. **Use sort keys** - Filter on sort key columns for best performance +3. **Test incrementally** - Start with simple queries, add complexity gradually while monitoring performance +4. **Monitor query costs** - Check `statistics.rows_read` and `statistics.bytes_read` +5. **Cache results** - Store frequently accessed results in your application +6. **Use CTEs** - Break complex queries into readable, optimized steps +7. **Handle pagination** - Use `LIMIT` and `OFFSET` for large result sets +8. **Validate data types** - Understand column types before conversions + +## Response Fields + +### data +Array of result rows matching your query. Each row is an object with column names as keys. + +### meta +Array of column metadata objects: +```json +{"name": "column_name", "type": "ClickHouse_type"} +``` + +### rows +Integer count of rows returned. + +### statistics +Query execution metrics: +- `elapsed`: Query execution time in seconds +- `rows_read`: Number of rows scanned +- `bytes_read`: Bytes of data scanned + +## Error Handling + +### Common Errors + +| Error | Cause | Solution | +|-------|-------|----------| +| Syntax error | Invalid SQL | Verify SQL syntax, check schema reference for correct column names and types | +| Timeout | Query too complex | Add time filters, reduce data scanned | +| Column not found | Typo in column name | Verify column names in schema | +| Type mismatch | Invalid type conversion | Check column types, use correct functions | +| Rate limit | Too many requests | Implement request throttling | + +### HTTP Status Codes + +- `200` - Success +- `400` - Bad request (SQL syntax error) +- `401` - Unauthorized (invalid API key) +- `429` - Rate limit exceeded +- `500` - Server error + +## Code Examples + +### cURL + +```bash +curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: ' \ + -d '{ + "query": "SELECT * FROM hyperliquid_trades ORDER BY block_time DESC LIMIT 10", + "clusterId": "hyperliquid-core-mainnet" + }' +``` + +### JavaScript + +```javascript +const response = await fetch('https://api.quicknode.com/sql/rest/v1/query', { + method: 'POST', + headers: { + 'accept': 'application/json', + 'Content-Type': 'application/json', + 'x-api-key': '' + }, + body: JSON.stringify({ + query: 'SELECT * FROM hyperliquid_trades ORDER BY block_time DESC LIMIT 10', + clusterId: 'hyperliquid-core-mainnet' + }) +}); + +const data = await response.json(); +console.log(data); +``` + +### Python + +```python +import requests +import json + +url = "https://api.quicknode.com/sql/rest/v1/query" + +payload = { + "query": "SELECT * FROM hyperliquid_trades ORDER BY block_time DESC LIMIT 10", + "clusterId": "hyperliquid-core-mainnet" +} + +headers = { + "accept": "application/json", + "Content-Type": "application/json", + "x-api-key": "" +} + +response = requests.post(url, json=payload, headers=headers) +print(response.json()) +``` + +### Ruby + +```ruby +require 'net/http' +require 'json' + +uri = URI('https://api.quicknode.com/sql/rest/v1/query') + +request = Net::HTTP::Post.new(uri) +request['accept'] = 'application/json' +request['Content-Type'] = 'application/json' +request['x-api-key'] = '' + +request.body = { + query: 'SELECT * FROM hyperliquid_trades ORDER BY block_time DESC LIMIT 10', + clusterId: 'hyperliquid-core-mainnet' +}.to_json + +response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) +end + +puts response.body +``` + +### Go + +```go +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +func main() { + url := "https://api.quicknode.com/sql/rest/v1/query" + + payload := map[string]string{ + "query": "SELECT * FROM hyperliquid_trades ORDER BY block_time DESC LIMIT 10", + "clusterId": "hyperliquid-core-mainnet", + } + + jsonData, _ := json.Marshal(payload) + + req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + req.Header.Set("accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("x-api-key", "") + + client := &http.Client{} + resp, _ := client.Do(req) + defer resp.Body.Close() + + var result map[string]interface{} + json.NewDecoder(resp.Body).Decode(&result) + fmt.Println(result) +} +``` + +## Use Cases + +### Trading Bots + +Monitor trades, orders, and fills to trigger automated trading strategies. + +```sql +-- Detect large market moves +SELECT + coin, + AVG(toFloat64(price)) AS avg_price, + COUNT(*) AS trade_count +FROM hyperliquid_trades +WHERE time >= now() - INTERVAL 5 MINUTE +GROUP BY coin +HAVING trade_count > 100 +``` + +### Analytics Dashboards + +Build real-time dashboards tracking market metrics. + +```sql +-- 24h volume by market +SELECT + coin, + SUM(toFloat64(size)) AS volume, + COUNT(*) AS trades +FROM hyperliquid_trades +WHERE time >= now() - INTERVAL 24 HOUR +GROUP BY coin +ORDER BY volume DESC +``` + +### Risk Monitoring + +Track positions, liquidations, and funding rates. + +```sql +-- Recent liquidations +SELECT + time, + coin, + side, + price, + size, + toFloat64(price) * toFloat64(size) AS notional, + liquidated_user, + liquidation_mark_price, + liquidation_method, + user +FROM hyperliquid_fills +WHERE block_time > now() - INTERVAL 24 HOUR + AND is_liquidation = 1 +ORDER BY block_number DESC, tid DESC +LIMIT 100 +``` + +### Market Research + +Historical analysis of trading patterns and market structure. + +```sql +-- Builder market share +SELECT + builder_address, + COUNT(*) * 100.0 / (SELECT COUNT(*) FROM hyperliquid_builder_fills) AS market_share +FROM hyperliquid_builder_fills +GROUP BY builder_address +ORDER BY market_share DESC +``` + +## Documentation + +- **SQL Explorer Overview**: https://www.quicknode.com/docs/sql-explorer +- **REST API Overview**: https://www.quicknode.com/docs/sql-explorer/using-rest-api +- **Schema Reference**: https://www.quicknode.com/docs/sql-explorer/schema-reference +- **Pre-Built Queries**: https://www.quicknode.com/docs/sql-explorer/hyperliquid-queries +- **llms.txt**: https://www.quicknode.com/docs/sql-explorer/llms.txt diff --git a/plugins/quicknode/skills/quicknode-skill/references/streams-reference.md b/plugins/quicknode/skills/quicknode-skill/references/streams-reference.md new file mode 100644 index 0000000..18f2bdf --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/streams-reference.md @@ -0,0 +1,472 @@ +# Quicknode Streams Reference + +Streams provide real-time & historical blockchain data pipelines that filter, transform, and deliver data to various destinations. + +## Stream Architecture + +``` +Blockchain → Quicknode → Filter Function → Transform → Destination + Node (Webhook/S3/DB) +``` + +## Stream Types + +When creating a stream via the API, specify the `dataset` parameter: + +| Stream Type | `dataset` Value | +|-------------|----------------| +| Block | `block` | +| Block with Receipts | `block_with_receipts` | +| Transaction | `transaction` | +| Logs | `log` | +| Receipt | `receipt` | + +### Block Streams + +Receive full block data including all transactions. + +```javascript +function main(stream) { + const block = stream.data; + + return { + blockNumber: block.number, + timestamp: block.timestamp, + transactionCount: block.transactions.length, + gasUsed: block.gasUsed, + baseFeePerGas: block.baseFeePerGas + }; +} +``` + +### Transaction Streams + +Receive individual transaction data. + +```javascript +function main(stream) { + const tx = stream.data; + + // Filter high-value transactions (> 10 ETH) + const value = BigInt(tx.value); + const threshold = BigInt('10000000000000000000'); // 10 ETH + + if (value > threshold) { + return { + hash: tx.hash, + from: tx.from, + to: tx.to, + value: tx.value, + gasPrice: tx.gasPrice + }; + } + + return null; // Filter out +} +``` + +### Logs Streams + +Receive contract event logs. + +```javascript +function main(stream) { + const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; + + const transfers = stream.data.filter(log => + log.topics[0] === TRANSFER_TOPIC + ); + + return transfers.map(log => ({ + contract: log.address, + from: '0x' + log.topics[1].slice(26), + to: '0x' + log.topics[2].slice(26), + value: log.data, + blockNumber: log.blockNumber, + transactionHash: log.transactionHash + })); +} +``` + +### Receipt Streams + +Receive transaction receipts with execution results. + +```javascript +function main(stream) { + const receipt = stream.data; + + return { + transactionHash: receipt.transactionHash, + status: receipt.status === '0x1' ? 'success' : 'failed', + gasUsed: receipt.gasUsed, + effectiveGasPrice: receipt.effectiveGasPrice, + logsCount: receipt.logs.length + }; +} +``` + +## Filter Functions + +### Function Signature + +```javascript +function main(stream) { + // stream.data - Blockchain data (block, tx, logs, receipt) + // stream.metadata - Stream metadata (network, streamId, etc.) + + // Return data to send to destination + // Return null to filter out + return processedData; +} +``` + +### Available Utilities + +```javascript +function main(stream) { + // BigInt for large numbers + const value = BigInt(stream.data.value); + + // Hex conversions + const decimal = parseInt(stream.data.gasUsed, 16); + + // String operations + const address = stream.data.to.toLowerCase(); + + return { value: value.toString(), decimal, address }; +} +``` + +### Complex Filter Example + +```javascript +function main(stream) { + // Monitor multiple DEX contracts for swap events + const DEX_CONTRACTS = [ + '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', // Uniswap V2 + '0xE592427A0AEce92De3Edee1F18E0157C05861564', // Uniswap V3 + '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F', // SushiSwap + ].map(a => a.toLowerCase()); + + const SWAP_TOPICS = [ + '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822', // V2 Swap + '0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67', // V3 Swap + ]; + + const swaps = stream.data.filter(log => + DEX_CONTRACTS.includes(log.address.toLowerCase()) && + SWAP_TOPICS.includes(log.topics[0]) + ); + + if (swaps.length === 0) return null; + + return swaps.map(log => ({ + dex: log.address, + txHash: log.transactionHash, + blockNumber: log.blockNumber, + topic: log.topics[0], + data: log.data + })); +} +``` + +## Destinations + +### Webhook + +Send data to HTTP endpoints. + +**Configuration:** +```json +{ + "type": "webhook", + "url": "https://your-server.com/webhook", + "headers": { + "Authorization": "Bearer YOUR_TOKEN", + "Content-Type": "application/json" + }, + "retryPolicy": { + "maxRetries": 3, + "backoffMs": 1000 + } +} +``` + +**Payload Format:** +```json +{ + "streamId": "stream_abc123", + "network": "ethereum-mainnet", + "dataset": "block", + "data": { /* your filtered/transformed data */ }, + "metadata": { + "blockNumber": 18000000, + "timestamp": 1693526400 + } +} +``` + +### Amazon S3 + +Store data in S3 buckets. + +**Configuration:** +```json +{ + "type": "s3", + "bucket": "your-bucket-name", + "region": "us-east-1", + "prefix": "blockchain-data/", + "format": "json", + "compression": "gzip", + "credentials": { + "accessKeyId": "YOUR_ACCESS_KEY", + "secretAccessKey": "YOUR_SECRET_KEY" + } +} +``` + +### PostgreSQL + +Insert data directly into PostgreSQL. + +**Configuration:** +```json +{ + "type": "postgresql", + "connectionString": "postgresql://user:pass@host:5432/db", + "table": "blockchain_events", + "schema": { + "tx_hash": "TEXT", + "block_number": "BIGINT", + "from_address": "TEXT", + "to_address": "TEXT", + "value": "NUMERIC", + "timestamp": "TIMESTAMP" + } +} +``` + +### Snowflake + +Stream to Snowflake data warehouse. + +**Configuration:** +```json +{ + "type": "snowflake", + "account": "your-account", + "warehouse": "COMPUTE_WH", + "database": "BLOCKCHAIN_DATA", + "schema": "PUBLIC", + "table": "EVENTS", + "credentials": { + "username": "YOUR_USERNAME", + "password": "YOUR_PASSWORD" + } +} +``` + +### Key-Value Store Integration + +Streams filter functions can use the `qnLib` helper to persist state across invocations via the Key-Value Store. This enables use cases like tracking seen addresses, maintaining watchlists, or accumulating counters. + +> **Important:** All `qnLib` methods are asynchronous and return Promises. Declare your filter function as `async function main(stream)` and use `await` on all `qnLib` calls. + +```javascript +async function main(stream) { + const tx = stream.data; + + // Check if sender is on our watchlist + const isWatched = await qnLib.qnContainsListItems('watchlist', [tx.from]); + + if (isWatched.includes(tx.from)) { + // Store the transaction hash in a set for later retrieval + await qnLib.qnAddSet('watched_txs', tx.hash, JSON.stringify({ + from: tx.from, + to: tx.to, + value: tx.value, + block: tx.blockNumber + })); + + return { + type: 'watchlist_hit', + hash: tx.hash, + from: tx.from, + to: tx.to, + value: tx.value + }; + } + + return null; +} +``` + +See the [Key-Value Store docs](https://www.quicknode.com/docs/key-value-store) for full `qnLib` method reference. + +## EVM Stream Examples + +### Monitor Specific Contract + +```javascript +function main(stream) { + const TARGET_CONTRACT = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; // USDT + + const logs = stream.data.filter(log => + log.address.toLowerCase() === TARGET_CONTRACT.toLowerCase() + ); + + return logs.length > 0 ? { events: logs } : null; +} +``` + +### Track Whale Transactions + +```javascript +function main(stream) { + const WHALE_THRESHOLD = BigInt('1000000000000000000000'); // 1000 ETH + + const tx = stream.data; + const value = BigInt(tx.value || '0'); + + if (value >= WHALE_THRESHOLD) { + return { + type: 'whale_transaction', + hash: tx.hash, + from: tx.from, + to: tx.to, + valueEth: (Number(value) / 1e18).toFixed(2) + }; + } + + return null; +} +``` + +### NFT Transfer Tracking + +```javascript +function main(stream) { + // ERC-721 Transfer event + const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; + + const nftTransfers = stream.data.filter(log => + log.topics[0] === TRANSFER_TOPIC && + log.topics.length === 4 // ERC-721 has tokenId in topics[3] + ); + + return nftTransfers.map(log => ({ + contract: log.address, + from: '0x' + log.topics[1].slice(26), + to: '0x' + log.topics[2].slice(26), + tokenId: BigInt(log.topics[3]).toString(), + txHash: log.transactionHash + })); +} +``` + +## Solana Stream Examples + +### Monitor Program Logs + +```javascript +function main(stream) { + const PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'; // SPL Token + + const programLogs = stream.data.filter(log => + log.programId === PROGRAM_ID + ); + + return programLogs.length > 0 ? { logs: programLogs } : null; +} +``` + +### Track SOL Transfers + +```javascript +function main(stream) { + const THRESHOLD = 1000000000000; // 1000 SOL in lamports + + const tx = stream.data; + + const transfers = tx.meta?.preBalances?.map((pre, i) => ({ + account: tx.transaction.message.accountKeys[i], + change: tx.meta.postBalances[i] - pre + })).filter(t => Math.abs(t.change) >= THRESHOLD); + + return transfers?.length > 0 ? { transfers } : null; +} +``` + +## Stream Management API + +### Create Stream + +```bash +curl -X POST https://api.quicknode.com/streams/rest/v1/streams \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "My Stream", + "network": "ethereum-mainnet", + "dataset": "receipts", + "filterFunction": "function main(stream) { return stream.data; }", + "destination": { + "type": "webhook", + "url": "https://your-server.com/webhook" + } + }' +``` + +### List Streams + +```bash +curl https://api.quicknode.com/streams/rest/v1/streams \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" +``` + +### Update Stream + +```bash +curl -X PATCH https://api.quicknode.com/streams/rest/v1/streams/{streamId} \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "status": "paused" + }' +``` + +### Delete Stream + +```bash +curl -X DELETE https://api.quicknode.com/streams/rest/v1/streams/{streamId} \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" +``` + +## Best Practices + +1. **Start narrow** - Begin with specific filters, expand as needed +2. **Test locally** - Validate filter functions before deployment +3. **Handle errors** - Implement proper error handling in destinations +4. **Monitor health** - Check stream status regularly in dashboard +5. **Use batching** - Batch webhook deliveries for high-volume streams +6. **Idempotency** - Design consumers to handle duplicate deliveries + +## Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| No data received | Filter too restrictive | Broaden filter conditions | +| Webhook failures | Endpoint unavailable | Check server health, increase timeout | +| Data lag | Processing backlog | Optimize filter function | +| Missing events | Incorrect topics | Verify event signatures | + +## Documentation + +- **Streams Overview**: https://www.quicknode.com/docs/streams +- **Streams Overview (llms.txt)**: https://www.quicknode.com/docs/streams/llms.txt +- **Filter Functions**: https://www.quicknode.com/docs/streams/filters +- **Destinations**: https://www.quicknode.com/docs/streams/destinations +- **API Reference**: https://www.quicknode.com/docs/streams/rest-api/getting-started +- **Guides**: https://www.quicknode.com/guides/tags/streams diff --git a/plugins/quicknode/skills/quicknode-skill/references/webhooks-reference.md b/plugins/quicknode/skills/quicknode-skill/references/webhooks-reference.md new file mode 100644 index 0000000..ea96aff --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/webhooks-reference.md @@ -0,0 +1,407 @@ +# Quicknode Webhooks Reference + +Webhooks provide event-driven notifications for blockchain activity, delivering data to your HTTP endpoints in real-time. + +## Webhooks vs Streams + +| Feature | Webhooks | Streams | +|---------|----------|---------| +| **Complexity** | Simple setup | More configuration | +| **Filtering** | Template-based | Custom JavaScript | +| **Destinations** | HTTP only | Webhook, S3, Postgres, Azure | +| **Transformation** | Limited | Full transformation | +| **Best For** | Simple alerts | Complex data pipelines | + +**Use Webhooks when:** You need simple, quick notifications for specific events. + +**Use Streams when:** You need complex filtering, data transformation, or non-HTTP destinations. + +> **Tip:** Need testnet tokens for webhook testing? Use the Quicknode faucet at https://faucet.quicknode.com/drip + +## Creating Webhooks + +### Via Dashboard + +1. Navigate to Quicknode Dashboard → Webhooks +2. Click "Create Webhook" +3. Select network (Ethereum, Polygon, Solana, etc.) +4. Choose a webhook template and configure filters +5. Set destination URL +6. Test and activate + +### Via API (Template-Based) + +Webhooks are created from predefined templates using `POST /webhooks/rest/v1/webhooks/template/{templateId}`. Each template accepts a `templateArgs` object for configuration. + +### Available Templates + +| Template ID | Chain | Description | +|-------------|-------|-------------| +| `evmWalletFilter` | EVM | Monitor transactions to/from specific wallet addresses | +| `evmContractEvents` | EVM | Monitor events emitted by specific contracts | +| `evmAbiFilter` | EVM | Filter by ABI-decoded function calls or events | +| `solanaWalletFilter` | Solana | Monitor transactions involving Solana wallet addresses | +| `bitcoinWalletFilter` | Bitcoin | Monitor transactions involving Bitcoin addresses | + +### Template Examples + +**EVM Wallet Monitoring:** + +```bash +curl -X POST https://api.quicknode.com/webhooks/rest/v1/webhooks/template/evmWalletFilter \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "My Wallet Monitor", + "network": "ethereum-mainnet", + "templateArgs": { + "addresses": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"] + }, + "destination_url": "https://your-server.com/webhook", + "enabled": true + }' +``` + +**EVM Contract Events:** + +```bash +curl -X POST https://api.quicknode.com/webhooks/rest/v1/webhooks/template/evmContractEvents \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "USDC Transfer Events", + "network": "ethereum-mainnet", + "templateArgs": { + "contracts": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], + "events": ["Transfer(address,address,uint256)"] + }, + "destination_url": "https://your-server.com/webhook", + "enabled": true + }' +``` + +**EVM ABI Filter:** + +```bash +curl -X POST https://api.quicknode.com/webhooks/rest/v1/webhooks/template/evmAbiFilter \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Uniswap Swaps", + "network": "ethereum-mainnet", + "templateArgs": { + "contracts": ["0x..."], + "abi": "[{\"type\":\"event\",\"name\":\"Swap\",...}]", + "events": ["Swap"] + }, + "destination_url": "https://your-server.com/webhook", + "enabled": true + }' +``` + +**Solana Wallet Monitoring:** + +```bash +curl -X POST https://api.quicknode.com/webhooks/rest/v1/webhooks/template/solanaWalletFilter \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Solana Wallet Monitor", + "network": "solana-mainnet", + "templateArgs": { + "addresses": ["YourSolanaWalletAddress..."] + }, + "destination_url": "https://your-server.com/webhook", + "enabled": true + }' +``` + +**Bitcoin Wallet Monitoring:** + +```bash +curl -X POST https://api.quicknode.com/webhooks/rest/v1/webhooks/template/bitcoinWalletFilter \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Bitcoin Wallet Monitor", + "network": "bitcoin-mainnet", + "templateArgs": { + "addresses": ["bc1q..."] + }, + "destination_url": "https://your-server.com/webhook", + "enabled": true + }' +``` + +## Payload Format + +### EVM Webhook Payload + +```json +{ + "webhookId": "webhook_abc123", + "network": "ethereum-mainnet", + "timestamp": 1693526400, + "blockNumber": 18000000, + "blockHash": "0x...", + "transactions": [ + { + "hash": "0x...", + "from": "0x...", + "to": "0x...", + "value": "1000000000000000000", + "gas": "21000", + "gasPrice": "50000000000", + "input": "0x...", + "nonce": 42, + "logs": [ + { + "address": "0x...", + "topics": [ + "0xddf252ad...", + "0x000...from", + "0x000...to" + ], + "data": "0x...", + "logIndex": 0 + } + ] + } + ] +} +``` + +### Solana Webhook Payload + +```json +{ + "webhookId": "webhook_xyz789", + "network": "solana-mainnet", + "timestamp": 1693526400, + "slot": 200000000, + "transactions": [ + { + "signature": "5K8Q...", + "meta": { + "fee": 5000, + "preBalances": [1000000000, 500000000], + "postBalances": [999995000, 500005000], + "err": null + }, + "transaction": { + "message": { + "accountKeys": ["Account1...", "Account2..."], + "instructions": [ + { + "programIdIndex": 0, + "accounts": [0, 1], + "data": "3Bxs..." + } + ] + }, + "signatures": ["5K8Q..."] + } + } + ] +} +``` + +## Receiving Webhooks + +### Express.js Server + +```javascript +const express = require('express'); +const crypto = require('crypto'); + +const app = express(); +app.use(express.json()); + +// Verify webhook signature using all three security headers +function verifySignature(payload, signature, nonce, timestamp, secret) { + const expectedSig = crypto + .createHmac('sha256', secret) + .update(nonce + timestamp + JSON.stringify(payload)) + .digest('hex'); + return crypto.timingSafeEqual( + Buffer.from(signature), + Buffer.from(expectedSig) + ); +} + +app.post('/webhook', (req, res) => { + const signature = req.headers['x-qn-signature']; + const nonce = req.headers['x-qn-nonce']; + const timestamp = req.headers['x-qn-timestamp']; + const secret = process.env.WEBHOOK_SECRET; + + // Verify signature (includes nonce + timestamp to prevent replay attacks) + if (!verifySignature(req.body, signature, nonce, timestamp, secret)) { + return res.status(401).json({ error: 'Invalid signature' }); + } + + // Process webhook + const { network, transactions } = req.body; + + for (const tx of transactions) { + console.log(`New transaction: ${tx.hash}`); + // Process transaction... + } + + res.status(200).json({ received: true }); +}); + +app.listen(3000); +``` + +### Python Flask Server + +```python +from flask import Flask, request, jsonify +import hmac +import hashlib +import os + +app = Flask(__name__) + +def verify_signature(payload, signature, nonce, timestamp, secret): + expected = hmac.new( + secret.encode(), + (nonce + timestamp + payload).encode(), + hashlib.sha256 + ).hexdigest() + return hmac.compare_digest(signature, expected) + +@app.route('/webhook', methods=['POST']) +def webhook(): + signature = request.headers.get('X-QN-Signature') + nonce = request.headers.get('X-QN-Nonce') + timestamp = request.headers.get('X-QN-Timestamp') + secret = os.environ.get('WEBHOOK_SECRET') + + if not verify_signature(request.data.decode(), signature, nonce, timestamp, secret): + return jsonify({'error': 'Invalid signature'}), 401 + + data = request.json + + for tx in data.get('transactions', []): + print(f"New transaction: {tx['hash']}") + # Process transaction... + + return jsonify({'received': True}), 200 + +if __name__ == '__main__': + app.run(port=3000) +``` + +## Webhook Management API + +### List Webhooks + +```bash +curl https://api.quicknode.com/webhooks/rest/v1/webhooks \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" +``` + +### Get Webhook + +```bash +curl https://api.quicknode.com/webhooks/rest/v1/webhooks/{webhookId} \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" +``` + +### Update Webhook + +```bash +curl -X PATCH https://api.quicknode.com/webhooks/rest/v1/webhooks/{webhookId} \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "enabled": false + }' +``` + +### Delete Webhook + +```bash +curl -X DELETE https://api.quicknode.com/webhooks/rest/v1/webhooks/{webhookId} \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" +``` + +### Test Webhook + +```bash +curl -X POST https://api.quicknode.com/webhooks/rest/v1/webhooks/{webhookId}/test \ + -H "Authorization: Bearer $QUICKNODE_API_KEY" +``` + +## Security Best Practices + +1. **Verify signatures** - Always validate using all three headers: `X-QN-Signature`, `X-QN-Nonce`, and `X-QN-Timestamp` +2. **Use HTTPS** - Never use HTTP endpoints for webhooks +3. **Timeout handling** - Respond within 30 seconds +4. **Idempotency** - Handle duplicate deliveries gracefully +5. **Error handling** - Return proper HTTP status codes +6. **Rate limiting** - Implement rate limiting on your endpoint + +## Retry Policy + +Quicknode automatically retries failed webhook deliveries: + +| Attempt | Delay | +|---------|-------| +| 1 | Immediate | +| 2 | 1 minute | +| 3 | 5 minutes | +| 4 | 30 minutes | +| 5 | 2 hours | + +After 5 failed attempts, the webhook is marked as failed. Check the dashboard for delivery status. + +## Common Use Cases + +### Transaction Alerts (Wallet Monitoring) + +Use the `evmWalletFilter` template with your wallet address to get notified of all incoming and outgoing transactions. For Solana, use `solanaWalletFilter`; for Bitcoin, use `bitcoinWalletFilter`. + +### NFT Sales Monitoring + +Use the `evmContractEvents` template with the marketplace contract address and the sale/transfer event signature to monitor NFT sales in real-time. + +### Token Transfers + +Use the `evmContractEvents` template with the token contract address and the `Transfer(address,address,uint256)` event to track all transfers for a specific ERC-20 or ERC-721 token. + +### DeFi Activity + +Use the `evmAbiFilter` template with the DEX contract address and the relevant ABI (e.g., `Swap` event) to monitor swaps, liquidity changes, and other DeFi activity. + +### Smart Contract Monitoring + +Use the `evmContractEvents` or `evmAbiFilter` template with a contract address to monitor all events emitted by a specific smart contract. Track state changes, admin actions, upgrades, or any on-chain activity tied to the contract. + +### Wallet Monitoring + +Use chain-specific wallet filters (`evmWalletFilter`, `solanaWalletFilter`, `bitcoinWalletFilter`) to track all activity for a specific wallet address, including incoming/outgoing transfers, token approvals, and contract interactions. + +### Account Monitoring + +Use webhooks to monitor specific accounts across chains. On Solana, track account data changes with `solanaAccountFilter`. On EVM chains, combine `evmWalletFilter` with `evmContractEvents` to get a complete view of an account's on-chain activity, including balance changes, token movements, and contract interactions. + +## Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| No deliveries | Template filters too narrow | Broaden template filters (add more addresses, use broader event signatures) | +| 401 errors | Invalid signature | Check secret configuration | +| Timeouts | Slow processing | Optimize handler, use async | +| Missing data | Incomplete template config | Add required addresses, events, or contract parameters | +| Duplicates | Retry deliveries | Implement idempotency | + +## Documentation + +- **Webhooks Overview**: https://www.quicknode.com/docs/webhooks +- **Webhooks Overview (llms.txt)**: https://www.quicknode.com/docs/webhooks/llms.txt +- **Webhook Templates API**: https://www.quicknode.com/docs/webhooks/rest-api/webhooks/webhooks-rest-create-from-template +- **API Reference**: https://www.quicknode.com/docs/webhooks/rest-api/getting-started +- **Guides**: https://www.quicknode.com/guides/tags/webhooks diff --git a/plugins/quicknode/skills/quicknode-skill/references/x402-reference.md b/plugins/quicknode/skills/quicknode-skill/references/x402-reference.md new file mode 100644 index 0000000..2f533c1 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/x402-reference.md @@ -0,0 +1,239 @@ +# x402 Reference + +x402 enables wallet-based RPC access via stablecoin payments. No API key required. Three payment models: pay-per-request (no auth), nanopayment (batched via Circle Gateway), and credit drawdown (SIWX auth + credit bundle). + +## Overview + +| Property | Value | +|----------|-------| +| **Protocol** | HTTP 402 Payment Required | +| **Payment Models** | Pay-per-request, Nanopayment, Credit Drawdown | +| **Authentication** | None (pay-per-request, nanopayment) or SIWX + JWT (credit drawdown) | +| **Supported Protocols** | JSON-RPC, REST, gRPC-Web, WebSocket (varies by payment model) | +| **Payment Networks** | Varies by payment model. See [Payment Networks](#payment-networks-caip-2) | +| **Chains** | All Quicknode-supported networks | +| **Base URL** | `https://x402.quicknode.com` | +| **Use Cases** | Keyless RPC access, AI agents, pay-as-you-go, ephemeral wallets | + +## Payment Models + +| Model | Auth | Cost | Protocols | Best For | +|-------|------|------|-----------|----------| +| **Pay-per-request** | None | $0.001/request | JSON-RPC, REST | Simple integrations, low volume | +| **Nanopayment** | None | $0.0001/request | JSON-RPC, REST | High-volume testing (EVM testnets only for payment chain) | +| **Credit Drawdown** | SIWX + JWT | Testnet: $1/1,000 credits. Mainnet: $10/1,000,000 credits | JSON-RPC, REST, gRPC-Web, WebSocket | Sustained usage, streaming protocols | + +The payment chain does not need to match the chain you query. For example, you can pay with Base Sepolia USDC and query Ethereum Mainnet. + +### Credit Drawdown Flow + +1. **Authenticate** via SIWX to get a JWT (1 hour expiry) +2. **Make a request** that returns HTTP 402 with payment requirements +3. **Pay** with a supported stablecoin to receive a credit bundle +4. **Consume credits** per successful response; when depleted, step 2 repeats automatically + +### Pay-per-request Flow + +1. **Make a request** (no auth needed); server returns HTTP 402 +2. **Pay** with a payment signature included in each request (handled automatically by the client) + +## Key Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/auth` | POST | Authenticate via SIWX, returns JWT (credit drawdown only) | +| `/credits` | GET | Check credit balance (credit drawdown only) | +| `/drip` | POST | Testnet USDC faucet (Base Sepolia only, requires JWT) | +| `/:network` | POST | JSON-RPC request to a specific chain (e.g., `/ethereum-mainnet`) | +| `/:network/ws` | WebSocket | WebSocket RPC connection to a specific chain | +| `/discovery/resources` | GET | Bazaar-compatible catalog of all supported networks | + +## Testnet Caps + +| Model | Testnet Lifetime Cap | +|-------|---------------------| +| Pay-per-request | 1,000 requests | +| Nanopayment | 10,000 requests | +| Credit Drawdown | 1,000 credits | + +Caps are tracked independently per model. After reaching a cap, switch to the corresponding mainnet. + +## Setup + +### Install Dependencies + +```bash +npm install @quicknode/x402 +``` + +The `@quicknode/x402` package handles payment negotiation for all three models. For credit drawdown, it also manages SIWX authentication and JWT sessions. + +### Create a Client and Make RPC Calls + +```typescript +import { createQuicknodeX402Client } from '@quicknode/x402'; + +const X402_BASE_URL = 'https://x402.quicknode.com'; + +// Credit drawdown: preAuth authenticates (SIWX + JWT) upfront for faster payment flow +const client = await createQuicknodeX402Client({ + baseUrl: X402_BASE_URL, + network: 'eip155:84532', // Base Sepolia (payment network) + evmPrivateKey: process.env.PRIVATE_KEY as `0x${string}`, + paymentModel: 'credit-drawdown', + preAuth: true, +}); + +// Make RPC calls (payment is automatic on 402) +const response = await client.fetch(`${X402_BASE_URL}/ethereum-mainnet`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_blockNumber', + params: [], + }), +}); + +const { result } = await response.json(); +console.log('Block number:', BigInt(result)); +``` + +### Client Configuration + +| Parameter | Description | +|-----------|-------------| +| `baseUrl` | The x402 gateway URL (`https://x402.quicknode.com`) | +| `network` | CAIP-2 chain identifier for the payment network | +| `evmPrivateKey` | Hex-encoded private key for EVM chains | +| `svmPrivateKey` | Base58-encoded secret key for Solana | +| `paymentModel` | `'pay-per-request'`, `'nanopayment'`, or `'credit-drawdown'` (default) | +| `preAuth` | Credit drawdown only. When `true`, pre-authenticates (SIWX + JWT) before the first request | + +### Solana Client + +```typescript +import { createQuicknodeX402Client } from '@quicknode/x402'; + +const client = await createQuicknodeX402Client({ + baseUrl: 'https://x402.quicknode.com', + network: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1', // Solana Devnet + svmPrivateKey: '', + preAuth: true, +}); + +// Query any chain (payment network doesn't need to match) +const response = await client.fetch('https://x402.quicknode.com/solana-devnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getSlot', params: [] }), +}); +``` + +### Pay-per-request Client (No Auth) + +```typescript +import { createQuicknodeX402Client } from '@quicknode/x402'; + +const client = await createQuicknodeX402Client({ + baseUrl: 'https://x402.quicknode.com', + network: 'eip155:84532', + evmPrivateKey: process.env.PRIVATE_KEY as `0x${string}`, + paymentModel: 'pay-per-request', +}); + +const response = await client.fetch('https://x402.quicknode.com/base-mainnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), +}); +``` + +### Nanopayment Client (Circle Gateway) + +Nanopayment requires a one-time USDC deposit into the Circle Gateway Wallet contract. After depositing, the Gateway API waits for a chain-specific number of block confirmations before updating your unified balance. See [Circle's supported blockchains reference](https://developers.circle.com/gateway/references/supported-blockchains#required-block-confirmations) for confirmation times per chain. + +```typescript +import { createQuicknodeX402Client } from "@quicknode/x402"; + +const client = await createQuicknodeX402Client({ + baseUrl: "https://x402.quicknode.com", + network: "eip155:84532", // Base Sepolia (must be a nanopayment-eligible EVM testnet) + evmPrivateKey: process.env.PRIVATE_KEY as `0x${string}`, + paymentModel: "nanopayment", +}); + +// Check Gateway Wallet balance and deposit if needed +if (client.gatewayClient) { + const balances = await client.gatewayClient.getBalances(); + console.log("Gateway available:", balances.gateway.formattedAvailable); + + // 1 USDC = 1_000_000 base units (6 decimals) + if (balances.gateway.available < 1_000_000n) { + console.log("Depositing 1 USDC..."); + const deposit = await client.gatewayClient.deposit("1"); + console.log(`Deposit tx: ${deposit.depositTxHash}`); + } +} + +const response = await client.fetch("https://x402.quicknode.com/base-mainnet", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_blockNumber", params: [] }), +}); + +const data = await response.json(); +console.log("Block:", data.result); +``` + +### Check Credit Balance (Credit Drawdown Only) + +```typescript +const creditsResponse = await fetch('https://x402.quicknode.com/credits', { + headers: { Authorization: `Bearer ${client.getToken()}` }, +}); +const { credits } = await creditsResponse.json(); +console.log('Remaining credits:', credits); +``` + +### Get Free Testnet Credits (Base Sepolia Only) + +```typescript +const dripResponse = await fetch('https://x402.quicknode.com/drip', { + method: 'POST', + headers: { Authorization: `Bearer ${client.getToken()}` }, +}); +const dripResult = await dripResponse.json(); +console.log('Credits received:', dripResult); +``` + +## Payment Networks (CAIP-2) + +| Network | CAIP-2 ID | Token | Pay-per-request | Credit Drawdown | Nanopayment | +|---------|-----------|-------|:---:|:---:|:---:| +| Base Sepolia | `eip155:84532` | USDC | Yes | Yes | Yes | +| Base Mainnet | `eip155:8453` | USDC | Yes | Yes | No | +| Polygon Amoy | `eip155:80002` | USDC | Yes | Yes | Yes | +| Polygon Mainnet | `eip155:137` | USDC | Yes | Yes | No | +| XLayer Testnet | `eip155:1952` | USDG | Yes | Yes | No | +| XLayer Mainnet | `eip155:196` | USDG | Yes | Yes | No | +| Arc Testnet | `eip155:5042002` | USDC | No | No | Yes | +| Solana Devnet | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | USDC | Yes | Yes | No | +| Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | USDC | Yes | Yes | No | + +## Best Practices + +1. **Start with pay-per-request** for simplicity (no auth needed), switch to credit drawdown for sustained usage or streaming protocols. +2. **Use testnet first.** Call `/drip` (Base Sepolia) or the [Circle faucet](https://faucet.circle.com/) to get testnet USDC before using mainnet. +3. **Use `preAuth: true`** with credit drawdown to speed up the first payment flow. +4. **Reuse session tokens.** The client caches JWTs automatically (1 hour expiry). +5. **gRPC-Web and WebSocket require credit drawdown.** Pay-per-request and nanopayment support JSON-RPC and REST only. +6. **Multi-protocol support.** The same client works with JSON-RPC, REST, gRPC-Web (`client.createGrpcTransport()`), and WebSocket (`client.createWebSocket()`). + +## Documentation + +- **x402 Platform**: https://x402.quicknode.com +- **x402 Documentation (llms.txt)**: https://x402.quicknode.com/llms.txt +- **@quicknode/x402 Package**: https://github.com/quiknode-labs/quicknode-x402 +- **Examples**: https://github.com/quiknode-labs/qn-x402-examples diff --git a/plugins/quicknode/skills/quicknode-skill/references/yellowstone-grpc-reference.md b/plugins/quicknode/skills/quicknode-skill/references/yellowstone-grpc-reference.md new file mode 100644 index 0000000..536e462 --- /dev/null +++ b/plugins/quicknode/skills/quicknode-skill/references/yellowstone-grpc-reference.md @@ -0,0 +1,492 @@ +# Yellowstone gRPC Reference + +Yellowstone gRPC is a high-performance Solana Geyser plugin that enables real-time blockchain data streaming through gRPC interfaces. Available as a Marketplace add-on on Quicknode. + +## Overview + +| Property | Value | +|----------|-------| +| **Protocol** | gRPC (HTTP/2) | +| **Port** | 10000 | +| **Package** | `@triton-one/yellowstone-grpc` (TypeScript) | +| **Compression** | zstd supported | +| **Commitment Levels** | Processed, Confirmed, Finalized | +| **Languages** | TypeScript, Rust, Go, Python | +| **Prerequisite** | Enable [Yellowstone Geyser gRPC add-on](https://marketplace.quicknode.com/add-on/yellowstone-grpc-geyser-plugin) on your Quicknode endpoint | + +## Endpoint & Authentication + +### Endpoint Format + +``` +https://.solana-mainnet.quiknode.pro:10000 +``` + +### Deriving Credentials + +From your HTTP Provider URL: +``` +https://example-guide-demo.solana-mainnet.quiknode.pro/123456789/ +``` + +- **Endpoint**: `https://example-guide-demo.solana-mainnet.quiknode.pro:10000` +- **Token**: `123456789` (the path segment after the endpoint name) + +## Installation + +### TypeScript + +```bash +npm install @triton-one/yellowstone-grpc +``` + +### Rust + +```toml +[dependencies] +yellowstone-grpc-client = "11.0.0" +yellowstone-grpc-proto = "10.1.1" +tokio = { version = "1.28" } +futures = "0.3" +``` + +### Go + +```bash +go get google.golang.org/grpc +go get google.golang.org/protobuf +``` + +Download proto files (`geyser.proto`, `solana-storage.proto`) from the [Yellowstone gRPC GitHub repo](https://github.com/rpcpool/yellowstone-grpc) and compile with `protoc`. + +### Python + +``` +grpcio==1.63.0 +grpcio-tools==1.63.0 +protobuf==5.26.1 +base58==2.1.1 +``` + +Generate stubs: +```bash +python -m grpc_tools.protoc \ + -I./proto/ \ + --python_out=./generated \ + --pyi_out=./generated \ + --grpc_python_out=./generated \ + ./proto/* +``` + +## Connection Setup + +### TypeScript + +```typescript +import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; + +const ENDPOINT = "https://example-guide-demo.solana-mainnet.quiknode.pro:10000"; +const TOKEN = "123456789"; + +const client = new Client(ENDPOINT, TOKEN, {}); +``` + +### Go + +```go +opts := []grpc.DialOption{ + grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 10 * time.Second, + Timeout: time.Second, + PermitWithoutStream: true, + }), + grpc.WithDefaultCallOptions( + grpc.MaxCallRecvMsgSize(1024 * 1024 * 1024), + grpc.UseCompressor(gzip.Name), + ), + grpc.WithPerRPCCredentials(tokenAuth{token: token}), +} + +conn, err := grpc.Dial(endpoint, opts...) +client := pb.NewGeyserClient(conn) +``` + +### Python + +```python +import grpc + +def create_grpc_channel(endpoint: str, token: str) -> grpc.Channel: + endpoint = endpoint.replace('http://', '').replace('https://', '') + auth_creds = grpc.metadata_call_credentials( + lambda context, callback: callback((("x-token", token),), None) + ) + ssl_creds = grpc.ssl_channel_credentials() + combined_creds = grpc.composite_channel_credentials(ssl_creds, auth_creds) + return grpc.secure_channel(endpoint, credentials=combined_creds) + +channel = create_grpc_channel( + "example-guide-demo.solana-mainnet.quiknode.pro:10000", + "123456789" +) +stub = geyser_pb2_grpc.GeyserStub(channel) +``` + +### Rust + +```rust +use yellowstone_grpc_client::GeyserGrpcClient; +use tonic::transport::ClientTlsConfig; + +let client = GeyserGrpcClient::build_from_shared(endpoint.to_string())? + .x_token(Some(token.to_string()))? + .tls_config(ClientTlsConfig::new().with_native_roots())? + .connect() + .await?; +``` + +## Subscribe Filter Types + +The `subscribe` method accepts a `SubscribeRequest` with the following filter maps: + +| Filter | Key | Description | +|--------|-----|-------------| +| **accounts** | `SubscribeRequestFilterAccounts` | Account data changes by pubkey, owner, or data filters | +| **transactions** | `SubscribeRequestFilterTransactions` | Transaction events with account/vote/failure filters | +| **transactionsStatus** | `SubscribeRequestFilterTransactions` | Lightweight transaction status updates (same filter shape) | +| **slots** | `SubscribeRequestFilterSlots` | Slot progression and status changes | +| **blocks** | `SubscribeRequestFilterBlocks` | Full block data with optional transaction/account inclusion | +| **blocksMeta** | `SubscribeRequestFilterBlocksMeta` | Block metadata without full contents | +| **entry** | `SubscribeRequestFilterEntry` | PoH entry updates | + +Global options on the request: + +| Field | Type | Description | +|-------|------|-------------| +| `commitment` | CommitmentLevel | PROCESSED (0), CONFIRMED (1), FINALIZED (2) | +| `accountsDataSlice` | Array | Slice account data: `{ offset, length }` | +| `ping` | Object | Keepalive ping: `{ id }` | +| `from_slot` | uint64 | Replay from a specific slot | + +## Transaction Filter Options + +| Field | Type | Description | +|-------|------|-------------| +| `vote` | bool (optional) | Include/exclude vote transactions | +| `failed` | bool (optional) | Include/exclude failed transactions | +| `signature` | string (optional) | Filter by specific transaction signature | +| `accountInclude` | string[] | Include transactions involving these accounts | +| `accountExclude` | string[] | Exclude transactions involving these accounts | +| `accountRequired` | string[] | Require all listed accounts in the transaction | + +## Account Filter Options + +| Field | Type | Description | +|-------|------|-------------| +| `account` | string[] | Filter by specific account pubkeys | +| `owner` | string[] | Filter by owner program pubkeys | +| `filters` | Array | Data filters: `memcmp`, `datasize`, `token_account_state`, `lamports` | +| `nonempty_txn_signature` | bool (optional) | Only accounts with non-empty transaction signatures | + +### Account Data Filters + +- **memcmp**: Match bytes at a specific offset (`{ offset, bytes | base58 | base64 }`) +- **datasize**: Match accounts with exact data size +- **token_account_state**: Match valid SPL token account state +- **lamports**: Compare lamport balance (`eq`, `ne`, `lt`, `gt`) + +## Available Methods + +| Method | Description | Parameters | +|--------|-------------|------------| +| `subscribe` | Bidirectional stream for real-time data | SubscribeRequest (via stream) | +| `subscribeReplayInfo` | Earliest available slot for replay | None | +| `getBlockHeight` | Current block height | Optional CommitmentLevel | +| `getLatestBlockhash` | Most recent blockhash | Optional CommitmentLevel | +| `getSlot` | Current slot number | Optional CommitmentLevel | +| `getVersion` | Geyser plugin version info | None | +| `isBlockhashValid` | Check blockhash validity | blockhash (string), optional CommitmentLevel | +| `ping` | Connection health check | count (integer) | + +## Subscription Examples + +### Account Updates + +```typescript +import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; + +const client = new Client(ENDPOINT, TOKEN, {}); +const stream = await client.subscribe(); + +stream.on("data", (data) => { + if (data.account) { + const account = data.account; + console.log("Account updated:", { + pubkey: Buffer.from(account.account.pubkey).toString("hex"), + lamports: account.account.lamports, + slot: account.slot, + owner: Buffer.from(account.account.owner).toString("hex"), + }); + } +}); + +stream.on("error", (error) => { + console.error("Stream error:", error); +}); + +await new Promise((resolve, reject) => { + stream.write( + { + accounts: { + account_filter: { + account: ["ACCOUNT_PUBKEY"], + owner: [], + filters: [], + }, + }, + slots: {}, + transactions: {}, + transactionsStatus: {}, + entry: {}, + blocks: {}, + blocksMeta: {}, + accountsDataSlice: [], + ping: undefined, + commitment: CommitmentLevel.CONFIRMED, + }, + (err) => { + if (err) reject(err); + else resolve(); + } + ); +}); +``` + +### Transaction Streaming + +```typescript +import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; + +const client = new Client(ENDPOINT, TOKEN, {}); +const stream = await client.subscribe(); + +stream.on("data", (data) => { + if (data.transaction) { + const txn = data.transaction; + console.log("Transaction:", { + signature: Buffer.from(txn.transaction.signature).toString("base64"), + slot: txn.slot, + isVote: txn.transaction.isVote, + }); + } +}); + +await new Promise((resolve, reject) => { + stream.write( + { + accounts: {}, + slots: {}, + transactions: { + txn_filter: { + vote: false, + failed: false, + accountInclude: ["PROGRAM_OR_ACCOUNT_PUBKEY"], + accountExclude: [], + accountRequired: [], + }, + }, + transactionsStatus: {}, + entry: {}, + blocks: {}, + blocksMeta: {}, + accountsDataSlice: [], + ping: undefined, + commitment: CommitmentLevel.CONFIRMED, + }, + (err) => { + if (err) reject(err); + else resolve(); + } + ); +}); +``` + +### Slot Updates + +```typescript +import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; + +const client = new Client(ENDPOINT, TOKEN, {}); +const stream = await client.subscribe(); + +stream.on("data", (data) => { + if (data.slot) { + console.log("Slot:", { + slot: data.slot.slot, + parent: data.slot.parent, + status: data.slot.status, + }); + } +}); + +await new Promise((resolve, reject) => { + stream.write( + { + accounts: {}, + slots: { + slot_filter: { + filterByCommitment: true, + }, + }, + transactions: {}, + transactionsStatus: {}, + entry: {}, + blocks: {}, + blocksMeta: {}, + accountsDataSlice: [], + ping: undefined, + commitment: CommitmentLevel.CONFIRMED, + }, + (err) => { + if (err) reject(err); + else resolve(); + } + ); +}); +``` + +### Unary RPC Methods + +```typescript +import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; + +const client = new Client(ENDPOINT, TOKEN, {}); + +// Block height +const blockHeight = await client.getBlockHeight(); +console.log("Block height:", blockHeight); + +// Latest blockhash +const blockhash = await client.getLatestBlockhash(CommitmentLevel.CONFIRMED); +console.log("Blockhash:", blockhash); + +// Current slot +const slot = await client.getSlot(); +console.log("Slot:", slot); + +// Version info +const version = await client.getVersion(); +console.log("Version:", version); + +// Validate blockhash +const valid = await client.isBlockhashValid(blockhash.blockhash); +console.log("Valid:", valid); + +// Ping +const pong = await client.ping(1); +console.log("Pong:", pong); + +// Replay info +const replayInfo = await client.subscribeReplayInfo({}); +console.log("First available slot:", replayInfo.firstAvailable); +``` + +## Stream Handling + +### Async Iteration Pattern + +```typescript +const stream = await client.subscribe(); + +// Write subscription request +stream.write(subscribeRequest); + +// Process updates +stream.on("data", (update) => { + if (update.account) handleAccount(update); + if (update.transaction) handleTransaction(update); + if (update.slot) handleSlot(update); + if (update.block) handleBlock(update); + if (update.blockMeta) handleBlockMeta(update); + if (update.entry) handleEntry(update); + if (update.pong) handlePong(update); +}); + +stream.on("error", (error) => { + console.error("Stream error:", error); + // Implement reconnection logic +}); + +stream.on("end", () => { + console.log("Stream ended"); + // Implement reconnection logic +}); +``` + +### Keepalive Pings + +```typescript +// Send periodic pings to keep the connection alive +const pingInterval = setInterval(() => { + stream.write({ + ping: { id: Date.now() }, + }); +}, 10000); // every 10 seconds + +// Clean up on stream end +stream.on("end", () => clearInterval(pingInterval)); +``` + +### Reconnection with Backoff + +```typescript +async function connectWithRetry(maxRetries = 5) { + let attempt = 0; + while (attempt < maxRetries) { + try { + const client = new Client(ENDPOINT, TOKEN, {}); + const stream = await client.subscribe(); + stream.write(subscribeRequest); + return stream; + } catch (error) { + attempt++; + const delay = Math.min(1000 * Math.pow(2, attempt), 30000); + console.error(`Connection failed (attempt ${attempt}), retrying in ${delay}ms`); + await new Promise((r) => setTimeout(r, delay)); + } + } + throw new Error("Max retries exceeded"); +} +``` + +## Best Practices + +1. **Use narrow filters** — Subscribe only to accounts, programs, or transaction patterns you need. Broad filters increase bandwidth and processing overhead. +2. **Set appropriate commitment levels** — Use `CONFIRMED` for most use cases. Use `FINALIZED` when you need irreversibility guarantees. Avoid `PROCESSED` unless you need the lowest latency and can handle rollbacks. +3. **Implement reconnection logic** — gRPC streams can drop due to network issues or server maintenance. Always implement exponential backoff reconnection. +4. **Enable zstd compression** — Reduces bandwidth significantly for high-throughput subscriptions. +5. **Test on devnet first** — Validate your filter logic and stream handling on devnet before deploying to mainnet. +6. **Use `accountsDataSlice`** — When you only need part of an account's data, slice it to reduce payload size. + +## Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| Connection refused on port 10000 | Yellowstone add-on not enabled | Enable the Yellowstone Geyser gRPC add-on in the Quicknode dashboard | +| Authentication failed | Invalid or missing token | Extract the token from your HTTP Provider URL (path segment after endpoint name) | +| No data received | Filters too restrictive or wrong commitment level | Start with broad filters and narrow down; check commitment level | +| Stream drops frequently | No keepalive pings | Send periodic pings (every 10s) and implement reconnection logic | +| Large payloads / high bandwidth | Subscribing to too much data | Narrow filters, use `accountsDataSlice`, enable zstd compression | +| Stale data | Using `PROCESSED` commitment | Switch to `CONFIRMED` or `FINALIZED` | + +## Documentation + +- **Yellowstone gRPC Overview**: https://www.quicknode.com/docs/solana/yellowstone-grpc/overview +- **Subscribe Method**: https://www.quicknode.com/docs/solana/yellowstone-grpc/subscribe +- **TypeScript Setup**: https://www.quicknode.com/docs/solana/yellowstone-grpc/overview/typescript +- **Go Setup**: https://www.quicknode.com/docs/solana/yellowstone-grpc/overview/go +- **Rust Setup**: https://www.quicknode.com/docs/solana/yellowstone-grpc/overview/rust +- **Python Setup**: https://www.quicknode.com/docs/solana/yellowstone-grpc/overview/python +- **Marketplace Add-on**: https://marketplace.quicknode.com/add-on/yellowstone-grpc-geyser-plugin +- **Guides**: https://www.quicknode.com/guides/tags/geyser diff --git a/scripts/gen-install-links.mjs b/scripts/gen-install-links.mjs index 23e1b43..86ed462 100644 --- a/scripts/gen-install-links.mjs +++ b/scripts/gen-install-links.mjs @@ -15,11 +15,11 @@ const PLUGIN_DISPLAY = "Quicknode MCP"; const WINDSURF_REGISTRY_NAME = "quicknode-mcp"; const mcpJson = JSON.parse( - readFileSync(resolve(repoRoot, "plugins/mcp/mcp.json"), "utf8") + readFileSync(resolve(repoRoot, "plugins/quicknode/mcp.json"), "utf8") ); const serverConfig = mcpJson.mcpServers[PLUGIN_NAME]; if (!serverConfig) { - throw new Error(`No server named "${PLUGIN_NAME}" in plugins/mcp/mcp.json`); + throw new Error(`No server named "${PLUGIN_NAME}" in plugins/quicknode/mcp.json`); } // --- Cursor deeplink (encodes the full config in base64) --- diff --git a/scripts/sync-skill.sh b/scripts/sync-skill.sh new file mode 100755 index 0000000..616834b --- /dev/null +++ b/scripts/sync-skill.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Syncs plugins/quicknode/skills/quicknode-skill/ from quiknode-labs/blockchain-skills@main. +# Exits non-zero if files changed (for CI drift detection). +set -euo pipefail + +OWNER="quiknode-labs" +REPO="blockchain-skills" +BRANCH="main" +# Upstream path within blockchain-skills (used for the remote fetch URLs). +UPSTREAM_SKILL_PATH="skills/quicknode-skill" +# Local path within this repo (where the synced files land + drift is checked). +SKILL_PATH="plugins/quicknode/skills/quicknode-skill" +API_BASE="https://api.github.com/repos/${OWNER}/${REPO}" +RAW_BASE="https://raw.githubusercontent.com/${OWNER}/${REPO}/${BRANCH}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TARGET_DIR="${SCRIPT_DIR}/../${SKILL_PATH}" + +# ---------- helpers ---------- + +need() { command -v "$1" &>/dev/null || { echo "ERROR: $1 is required but not installed." >&2; exit 1; }; } +need curl +need python3 + +fetch_json() { curl -fsSL "${1}" ${GITHUB_TOKEN:+--header "Authorization: Bearer ${GITHUB_TOKEN}"}; } +fetch_raw() { curl -fsSL "${1}" ${GITHUB_TOKEN:+--header "Authorization: Bearer ${GITHUB_TOKEN}"}; } + +# ---------- resolve upstream commit SHA ---------- + +echo "→ Resolving upstream commit SHA for ${OWNER}/${REPO}@${BRANCH}/${UPSTREAM_SKILL_PATH} ..." +COMMIT_SHA=$(fetch_json "${API_BASE}/commits?path=${UPSTREAM_SKILL_PATH}&per_page=1&sha=${BRANCH}" \ + | python3 -c "import json,sys; data=json.load(sys.stdin); print(data[0]['sha'][:12])" 2>/dev/null \ + || echo "unknown") +echo " SHA: ${COMMIT_SHA}" + +# ---------- fetch SKILL.md ---------- + +echo "→ Fetching SKILL.md ..." +mkdir -p "${TARGET_DIR}" +fetch_raw "${RAW_BASE}/${UPSTREAM_SKILL_PATH}/SKILL.md" > "${TARGET_DIR}/SKILL.md" + +# ---------- fetch references/ ---------- + +echo "→ Fetching references/ ..." +REF_LIST=$(fetch_json "${API_BASE}/contents/${UPSTREAM_SKILL_PATH}/references?ref=${BRANCH}" \ + | python3 -c " +import json, sys +data = json.load(sys.stdin) +if isinstance(data, list): + for f in data: + if f['type'] == 'file': + print(f['name']) +") + +mkdir -p "${TARGET_DIR}/references" + +for fname in ${REF_LIST}; do + echo " ↓ references/${fname}" + fetch_raw "${RAW_BASE}/${UPSTREAM_SKILL_PATH}/references/${fname}" > "${TARGET_DIR}/references/${fname}" +done + +# ---------- write provenance ---------- + +SYNC_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +cat > "${TARGET_DIR}/SKILL_SOURCE.md" < +- Upstream repo: https://github.com/${OWNER}/${REPO} +- Upstream path: ${UPSTREAM_SKILL_PATH} +- Upstream commit: ${COMMIT_SHA} +- Synced at: ${SYNC_DATE} +EOF + +echo "→ Wrote provenance to ${TARGET_DIR}/SKILL_SOURCE.md" +echo " Upstream commit: ${COMMIT_SHA}" +echo " Synced at: ${SYNC_DATE}" + +# ---------- exit non-zero if git sees changes (CI drift check) ---------- + +if git -C "${SCRIPT_DIR}/.." rev-parse --is-inside-work-tree &>/dev/null; then + CHANGED=$(git -C "${SCRIPT_DIR}/.." diff --name-only -- "${SKILL_PATH}" 2>/dev/null \ + ; git -C "${SCRIPT_DIR}/.." ls-files --others --exclude-standard -- "${SKILL_PATH}" 2>/dev/null) + if [ -n "${CHANGED}" ]; then + echo "" + echo "DRIFT DETECTED — the following files differ from HEAD:" + echo "${CHANGED}" | sed 's/^/ /' + echo "" + echo "Commit or PR these changes to resolve drift." + exit 1 + fi +fi + +echo "✓ Skill sync complete — no drift detected."