quicknode-sdk 0.4.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/Cargo.lock +30 -19
  2. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/Cargo.toml +4 -1
  3. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/PKG-INFO +41 -2
  4. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/Cargo.toml +1 -1
  5. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/README.md +43 -1
  6. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/examples/admin_e2e.rs +1 -0
  7. quicknode_sdk-0.5.0/crates/core/examples/sql.rs +70 -0
  8. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/mod.rs +2 -0
  9. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/config.rs +25 -1
  10. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/kvstore/mod.rs +1 -0
  11. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/lib.rs +20 -2
  12. quicknode_sdk-0.5.0/crates/core/src/sql/mod.rs +466 -0
  13. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/streams/mod.rs +1 -0
  14. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/webhooks/mod.rs +1 -0
  15. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/python/Cargo.toml +1 -0
  16. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/python/src/lib.rs +68 -1
  17. quicknode_sdk-0.5.0/crates/python/src/sql.rs +59 -0
  18. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/pyproject.toml +1 -1
  19. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/python/README.md +40 -1
  20. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/python/quicknode_sdk/__init__.py +16 -0
  21. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/python/quicknode_sdk/__init__.pyi +16 -0
  22. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/python/quicknode_sdk/_core/__init__.pyi +246 -1
  23. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/python/quicknode_sdk/init_manual_override.pyi +16 -0
  24. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/LICENSE +0 -0
  25. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/LICENSE +0 -0
  26. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/examples/admin.rs +0 -0
  27. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/examples/kvstore_e2e.rs +0 -0
  28. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/examples/streams.rs +0 -0
  29. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/examples/streams_e2e.rs +0 -0
  30. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/examples/webhooks_e2e.rs +0 -0
  31. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/billing.rs +0 -0
  32. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/bulk.rs +0 -0
  33. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/chains.rs +0 -0
  34. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/endpoint_metrics.rs +0 -0
  35. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/endpoint_rate_limits.rs +0 -0
  36. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/endpoint_security.rs +0 -0
  37. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/endpoint_urls.rs +0 -0
  38. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/endpoints.rs +0 -0
  39. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/logs.rs +0 -0
  40. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/tags.rs +0 -0
  41. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/teams.rs +0 -0
  42. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/admin/usage.rs +0 -0
  43. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/errors.rs +0 -0
  44. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/streams/stream.rs +0 -0
  45. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/core/src/webhooks/webhook.rs +0 -0
  46. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/python/src/errors.rs +0 -0
  47. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/python/src/streams_destination.rs +0 -0
  48. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/crates/python/src/webhooks_template.rs +0 -0
  49. {quicknode_sdk-0.4.0 → quicknode_sdk-0.5.0}/python/quicknode_sdk/py.typed +0 -0
@@ -1085,9 +1085,9 @@ dependencies = [
1085
1085
 
1086
1086
  [[package]]
1087
1087
  name = "js-sys"
1088
- version = "0.3.102"
1088
+ version = "0.3.103"
1089
1089
  source = "registry+https://github.com/rust-lang/crates.io-index"
1090
- checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
1090
+ checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
1091
1091
  dependencies = [
1092
1092
  "cfg-if",
1093
1093
  "futures-util",
@@ -1694,9 +1694,19 @@ dependencies = [
1694
1694
  "syn",
1695
1695
  ]
1696
1696
 
1697
+ [[package]]
1698
+ name = "pythonize"
1699
+ version = "0.27.0"
1700
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1701
+ checksum = "a3a8f29db331e28c332c63496cfcbb822aca3d7320bc08b655d7fd0c29c50ede"
1702
+ dependencies = [
1703
+ "pyo3",
1704
+ "serde",
1705
+ ]
1706
+
1697
1707
  [[package]]
1698
1708
  name = "quicknode-sdk"
1699
- version = "0.4.0"
1709
+ version = "0.5.0"
1700
1710
  dependencies = [
1701
1711
  "bon",
1702
1712
  "config",
@@ -2173,7 +2183,7 @@ dependencies = [
2173
2183
 
2174
2184
  [[package]]
2175
2185
  name = "sdk-node"
2176
- version = "0.4.0"
2186
+ version = "0.5.0"
2177
2187
  dependencies = [
2178
2188
  "napi",
2179
2189
  "napi-build",
@@ -2185,18 +2195,19 @@ dependencies = [
2185
2195
 
2186
2196
  [[package]]
2187
2197
  name = "sdk-python"
2188
- version = "0.4.0"
2198
+ version = "0.5.0"
2189
2199
  dependencies = [
2190
2200
  "pyo3",
2191
2201
  "pyo3-async-runtimes",
2192
2202
  "pyo3-stub-gen",
2203
+ "pythonize",
2193
2204
  "quicknode-sdk",
2194
2205
  "serde_json",
2195
2206
  ]
2196
2207
 
2197
2208
  [[package]]
2198
2209
  name = "sdk-python-stubs"
2199
- version = "0.4.0"
2210
+ version = "0.5.0"
2200
2211
  dependencies = [
2201
2212
  "pyo3-stub-gen",
2202
2213
  "sdk-python",
@@ -2204,7 +2215,7 @@ dependencies = [
2204
2215
 
2205
2216
  [[package]]
2206
2217
  name = "sdk-ruby"
2207
- version = "0.4.0"
2218
+ version = "0.5.0"
2208
2219
  dependencies = [
2209
2220
  "magnus",
2210
2221
  "quicknode-sdk",
@@ -2919,9 +2930,9 @@ dependencies = [
2919
2930
 
2920
2931
  [[package]]
2921
2932
  name = "wasm-bindgen"
2922
- version = "0.2.125"
2933
+ version = "0.2.126"
2923
2934
  source = "registry+https://github.com/rust-lang/crates.io-index"
2924
- checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
2935
+ checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
2925
2936
  dependencies = [
2926
2937
  "cfg-if",
2927
2938
  "once_cell",
@@ -2932,9 +2943,9 @@ dependencies = [
2932
2943
 
2933
2944
  [[package]]
2934
2945
  name = "wasm-bindgen-futures"
2935
- version = "0.4.75"
2946
+ version = "0.4.76"
2936
2947
  source = "registry+https://github.com/rust-lang/crates.io-index"
2937
- checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280"
2948
+ checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
2938
2949
  dependencies = [
2939
2950
  "js-sys",
2940
2951
  "wasm-bindgen",
@@ -2942,9 +2953,9 @@ dependencies = [
2942
2953
 
2943
2954
  [[package]]
2944
2955
  name = "wasm-bindgen-macro"
2945
- version = "0.2.125"
2956
+ version = "0.2.126"
2946
2957
  source = "registry+https://github.com/rust-lang/crates.io-index"
2947
- checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
2958
+ checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
2948
2959
  dependencies = [
2949
2960
  "quote",
2950
2961
  "wasm-bindgen-macro-support",
@@ -2952,9 +2963,9 @@ dependencies = [
2952
2963
 
2953
2964
  [[package]]
2954
2965
  name = "wasm-bindgen-macro-support"
2955
- version = "0.2.125"
2966
+ version = "0.2.126"
2956
2967
  source = "registry+https://github.com/rust-lang/crates.io-index"
2957
- checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
2968
+ checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
2958
2969
  dependencies = [
2959
2970
  "bumpalo",
2960
2971
  "proc-macro2",
@@ -2965,18 +2976,18 @@ dependencies = [
2965
2976
 
2966
2977
  [[package]]
2967
2978
  name = "wasm-bindgen-shared"
2968
- version = "0.2.125"
2979
+ version = "0.2.126"
2969
2980
  source = "registry+https://github.com/rust-lang/crates.io-index"
2970
- checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
2981
+ checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
2971
2982
  dependencies = [
2972
2983
  "unicode-ident",
2973
2984
  ]
2974
2985
 
2975
2986
  [[package]]
2976
2987
  name = "web-sys"
2977
- version = "0.3.102"
2988
+ version = "0.3.103"
2978
2989
  source = "registry+https://github.com/rust-lang/crates.io-index"
2979
- checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d"
2990
+ checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
2980
2991
  dependencies = [
2981
2992
  "js-sys",
2982
2993
  "wasm-bindgen",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/core", "crates/python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.4.0"
6
+ version = "0.5.0"
7
7
  edition = "2021"
8
8
  license = "MIT"
9
9
 
@@ -20,6 +20,9 @@ needless_pass_by_value = "warn"
20
20
  [workspace.dependencies]
21
21
  pyo3 = { version = "0.27", features = ["experimental-async"] }
22
22
  pyo3-async-runtimes = { version = "0.27", features = ["tokio-runtime"] }
23
+ # Tracks pyo3 versions in lockstep; converts dynamic SQL result rows
24
+ # (serde_json::Value) to native Python objects.
25
+ pythonize = { version = "0.27" }
23
26
  napi = { version = "3", features = ["async", "tokio_rt", "compat-mode"] }
24
27
  napi-derive = { version = "3", features = ["compat-mode"] }
25
28
  pyo3-stub-gen = { version = "0.19.0" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quicknode-sdk
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Classifier: Intended Audience :: Developers
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Operating System :: POSIX :: Linux
@@ -70,6 +70,7 @@ This is one of four language bindings published from the same Rust core. See the
70
70
  - [KV Store Client](#kv-store-client)
71
71
  - [Sets](#sets)
72
72
  - [Lists](#lists)
73
+ - [SQL Client](#sql-client)
73
74
  - [Error Handling](#error-handling)
74
75
  - [License](#license)
75
76
 
@@ -79,7 +80,7 @@ This is one of four language bindings published from the same Rust core. See the
79
80
 
80
81
  ## Quick Start
81
82
 
82
- Construct the SDK once, then reach into the four sub-clients (`admin`, `streams`, `webhooks`, `kvstore`). Subsequent API Reference snippets assume you have a `qn` handle from one of these blocks.
83
+ Construct the SDK once, then reach into the five sub-clients (`admin`, `streams`, `webhooks`, `kvstore`, `sql`). Subsequent API Reference snippets assume you have a `qn` handle from one of these blocks.
83
84
 
84
85
  ```python
85
86
  # Python
@@ -124,6 +125,7 @@ Environment variables (prefix `QN_SDK__`, separator `__`):
124
125
  | `QN_SDK__STREAMS__BASE_URL` | no | `https://api.quicknode.com/streams/rest/v1/` | Override streams base URL |
125
126
  | `QN_SDK__WEBHOOKS__BASE_URL` | no | `https://api.quicknode.com/webhooks/rest/v1/` | Override webhooks base URL |
126
127
  | `QN_SDK__KVSTORE__BASE_URL` | no | `https://api.quicknode.com/kv/rest/v1/` | Override KV store base URL |
128
+ | `QN_SDK__SQL__BASE_URL` | no | `https://api.quicknode.com/sql/rest/v1/` | Override SQL Explorer base URL |
127
129
  | `QN_SDK__HTTP__HEADERS__<NAME>` | no | — | Custom HTTP header sent on every request. Overrides SDK-managed headers (see below). |
128
130
 
129
131
  ### Custom headers and `User-Agent`
@@ -1630,6 +1632,43 @@ Deletes a list and all of its items.
1630
1632
  await qn.kvstore.delete_list("my-list")
1631
1633
  ```
1632
1634
 
1635
+ ---
1636
+
1637
+ ### SQL Client
1638
+
1639
+ Accessed as `qn.sql`. Runs SQL queries against indexed blockchain data and fetches the database schema. Backed by `https://api.quicknode.com/sql/rest/v1/`.
1640
+
1641
+ ##### `query`
1642
+
1643
+ Executes a SQL query against a cluster and returns the result set. Paginate by writing `LIMIT`/`OFFSET` into the SQL.
1644
+
1645
+ **Parameters**: `query` (str, required), `cluster_id` (str, required).
1646
+
1647
+ **Returns**: `QueryResponse` — `meta` (list of `ColumnMeta`, each with `name` and `column_type`), `data` (`list[dict]`, rows keyed by column name), `rows`, `rows_before_limit_at_least`, `statistics` (`QueryStatistics` with `elapsed`, `rows_read`, `bytes_read`), and `credits`.
1648
+
1649
+ ```python
1650
+ # Python
1651
+ resp = await qn.sql.query(
1652
+ query="SELECT action_type, user FROM hyperliquid_system_actions ORDER BY block_time DESC LIMIT 100",
1653
+ cluster_id="hyperliquid-core-mainnet",
1654
+ )
1655
+ print(resp.rows, resp.data[0])
1656
+ ```
1657
+
1658
+ ##### `get_schema`
1659
+
1660
+ Fetches the database schema for a cluster: table names, columns, types, sort keys, and partition strategies.
1661
+
1662
+ **Parameters**: `cluster_id` (str, required).
1663
+
1664
+ **Returns**: `ChainSchema` — `chain`, `cluster_id`, and `tables` (list of `TableSchema`, each with `name`, `engine`, `total_rows`, `partition_key`, `sorting_key`, and `columns` of `ColumnSchema` with `name` and `column_type`).
1665
+
1666
+ ```python
1667
+ # Python
1668
+ schema = await qn.sql.get_schema("hyperliquid-core-mainnet")
1669
+ print(len(schema.tables))
1670
+ ```
1671
+
1633
1672
  ## Error Handling
1634
1673
 
1635
1674
  Every binding exposes a typed exception hierarchy derived from the core `SdkError`
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "quicknode-sdk"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  description = "Core library for quicknode sdk"
@@ -46,6 +46,7 @@ This is one of four language bindings published from the same Rust core. See the
46
46
  - [KV Store Client](#kv-store-client)
47
47
  - [Sets](#sets)
48
48
  - [Lists](#lists)
49
+ - [SQL Client](#sql-client)
49
50
  - [Error Handling](#error-handling)
50
51
  - [License](#license)
51
52
 
@@ -55,7 +56,7 @@ This is one of four language bindings published from the same Rust core. See the
55
56
 
56
57
  ## Quick Start
57
58
 
58
- Construct the SDK once, then reach into the four sub-clients (`admin`, `streams`, `webhooks`, `kvstore`). Subsequent API Reference snippets assume you have a `qn` handle from one of these blocks.
59
+ Construct the SDK once, then reach into the five sub-clients (`admin`, `streams`, `webhooks`, `kvstore`, `sql`). Subsequent API Reference snippets assume you have a `qn` handle from one of these blocks.
59
60
 
60
61
  ```rust
61
62
  // Rust
@@ -99,6 +100,7 @@ Environment variables (prefix `QN_SDK__`, separator `__`):
99
100
  | `QN_SDK__STREAMS__BASE_URL` | no | `https://api.quicknode.com/streams/rest/v1/` | Override streams base URL |
100
101
  | `QN_SDK__WEBHOOKS__BASE_URL` | no | `https://api.quicknode.com/webhooks/rest/v1/` | Override webhooks base URL |
101
102
  | `QN_SDK__KVSTORE__BASE_URL` | no | `https://api.quicknode.com/kv/rest/v1/` | Override KV store base URL |
103
+ | `QN_SDK__SQL__BASE_URL` | no | `https://api.quicknode.com/sql/rest/v1/` | Override SQL Explorer base URL |
102
104
  | `QN_SDK__HTTP__HEADERS__<NAME>` | no | — | Custom HTTP header sent on every request. Overrides SDK-managed headers (see below). |
103
105
 
104
106
  ### Custom headers and `User-Agent`
@@ -1689,6 +1691,46 @@ Deletes a list and all of its items.
1689
1691
  qn.kvstore.delete_list("my-list").await?;
1690
1692
  ```
1691
1693
 
1694
+ ---
1695
+
1696
+ ### SQL Client
1697
+
1698
+ Accessed as `qn.sql`. Runs SQL queries against indexed blockchain data and fetches the database schema. Backed by `https://api.quicknode.com/sql/rest/v1/`.
1699
+
1700
+ ##### `query`
1701
+
1702
+ Executes a SQL query against a cluster and returns the result set. Paginate by writing `LIMIT`/`OFFSET` into the SQL.
1703
+
1704
+ **Parameters**: `QueryParams` with `query` (String, required) and `cluster_id` (String, required).
1705
+
1706
+ **Returns**: `QueryResponse` — `meta` (`Vec<ColumnMeta>`, each with `name` and `column_type`), `data` (`Vec<serde_json::Value>`, rows as JSON objects keyed by column name), `rows`, `rows_before_limit_at_least`, `statistics` (`QueryStatistics` with `elapsed`, `rows_read`, `bytes_read`), and `credits`.
1707
+
1708
+ ```rust
1709
+ // Rust
1710
+ let resp = qn
1711
+ .sql
1712
+ .query(&QueryParams {
1713
+ query: "SELECT action_type, user FROM hyperliquid_system_actions ORDER BY block_time DESC LIMIT 100".to_string(),
1714
+ cluster_id: "hyperliquid-core-mainnet".to_string(),
1715
+ })
1716
+ .await?;
1717
+ println!("{} rows, {:?}", resp.rows, resp.data.first());
1718
+ ```
1719
+
1720
+ ##### `get_schema`
1721
+
1722
+ Fetches the database schema for a cluster: table names, columns, types, sort keys, and partition strategies.
1723
+
1724
+ **Parameters**: `cluster_id` (`&str`, required).
1725
+
1726
+ **Returns**: `ChainSchema` — `chain`, `cluster_id`, and `tables` (`Vec<TableSchema>`, each with `name`, `engine`, `total_rows`, `partition_key`, `sorting_key`, and `columns` of `ColumnSchema { name, column_type }`).
1727
+
1728
+ ```rust
1729
+ // Rust
1730
+ let schema = qn.sql.get_schema("hyperliquid-core-mainnet").await?;
1731
+ println!("{} tables", schema.tables.len());
1732
+ ```
1733
+
1692
1734
  ## Error Handling
1693
1735
 
1694
1736
  Every binding exposes a typed exception hierarchy derived from the core `SdkError`
@@ -873,6 +873,7 @@ async fn main() {
873
873
  streams: None,
874
874
  webhooks: None,
875
875
  kvstore: None,
876
+ sql: None,
876
877
  };
877
878
  let headered = QuicknodeSdk::new(&with_headers).expect("build sdk with custom headers");
878
879
  match headered
@@ -0,0 +1,70 @@
1
+ use quicknode_sdk::{errors::SdkError, sql::QueryParams, QuicknodeSdk, SdkFullConfig};
2
+
3
+ const CLUSTER_ID: &str = "hyperliquid-core-mainnet";
4
+
5
+ #[tokio::main]
6
+ #[allow(clippy::unwrap_used, clippy::expect_used)]
7
+ async fn main() {
8
+ let config = SdkFullConfig::from_env().expect("Config from env failed");
9
+ let qn = QuicknodeSdk::new(&config).expect("sdk failed to initialize");
10
+
11
+ // ── Query ─────────────────────────────────────────────────────────────────
12
+
13
+ let params = QueryParams {
14
+ query: "SELECT toDateTime(block_time) AS time, action_type, user \
15
+ FROM hyperliquid_system_actions \
16
+ ORDER BY block_time DESC LIMIT 3"
17
+ .to_string(),
18
+ cluster_id: CLUSTER_ID.to_string(),
19
+ };
20
+
21
+ match qn.sql.query(&params).await {
22
+ Ok(resp) => {
23
+ println!(
24
+ "query: {} rows ({} before limit), {} credits, {:.4}s",
25
+ resp.rows, resp.rows_before_limit_at_least, resp.credits, resp.statistics.elapsed
26
+ );
27
+ println!(
28
+ "columns: {:?}",
29
+ resp.meta.iter().map(|c| &c.name).collect::<Vec<_>>()
30
+ );
31
+ // Read a value out of a dynamic row to confirm the conversion works.
32
+ if let Some(row) = resp.data.first() {
33
+ println!("first row action_type: {}", row["action_type"]);
34
+ }
35
+ }
36
+ Err(e) => eprintln!("query error: {e}"),
37
+ }
38
+
39
+ // ── Schema ──────────────────────────────────────────────────────────────
40
+
41
+ match qn.sql.get_schema(CLUSTER_ID).await {
42
+ Ok(schema) => {
43
+ println!("schema: {} ({} tables)", schema.chain, schema.tables.len());
44
+ if let Some(table) = schema.tables.first() {
45
+ println!(
46
+ "first table: {} ({} columns, {} rows)",
47
+ table.name,
48
+ table.columns.len(),
49
+ table.total_rows
50
+ );
51
+ }
52
+ }
53
+ Err(e) => eprintln!("get_schema error: {e}"),
54
+ }
55
+
56
+ // ── Error handling ────────────────────────────────────────────────────────
57
+
58
+ // An empty query is rejected with a 403 and a JSON body carrying the error
59
+ // message.
60
+ let bad = QueryParams {
61
+ query: String::new(),
62
+ cluster_id: CLUSTER_ID.to_string(),
63
+ };
64
+ match qn.sql.query(&bad).await {
65
+ Err(SdkError::Api { status, body }) => {
66
+ println!("api error {status}: {}", &body[..body.len().min(120)]);
67
+ }
68
+ other => eprintln!("expected Api error, got {other:?}"),
69
+ }
70
+ }
@@ -1903,6 +1903,7 @@ mod tests {
1903
1903
  streams: None,
1904
1904
  webhooks: None,
1905
1905
  kvstore: None,
1906
+ sql: None,
1906
1907
  })
1907
1908
  .unwrap()
1908
1909
  }
@@ -3673,6 +3674,7 @@ mod tests {
3673
3674
  streams: None,
3674
3675
  webhooks: None,
3675
3676
  kvstore: None,
3677
+ sql: None,
3676
3678
  });
3677
3679
  assert!(matches!(result, Err(crate::errors::SdkError::Config(_))));
3678
3680
  }
@@ -140,6 +140,26 @@ impl KvStoreConfig {
140
140
  }
141
141
  }
142
142
 
143
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
144
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
145
+ #[cfg_attr(feature = "node", napi(object))]
146
+ #[cfg_attr(feature = "rust", derive(Builder))]
147
+ #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
148
+ pub struct SqlConfig {
149
+ pub base_url: Option<String>,
150
+ }
151
+
152
+ #[cfg(feature = "python")]
153
+ #[gen_stub_pymethods]
154
+ #[pymethods]
155
+ impl SqlConfig {
156
+ #[new]
157
+ #[pyo3(signature = (base_url=None))]
158
+ pub fn new(base_url: Option<String>) -> Self {
159
+ SqlConfig { base_url }
160
+ }
161
+ }
162
+
143
163
  #[cfg_attr(feature = "python", gen_stub_pyclass)]
144
164
  #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
145
165
  #[cfg_attr(feature = "node", napi(object))]
@@ -152,6 +172,7 @@ pub struct SdkFullConfig {
152
172
  pub streams: Option<StreamsConfig>,
153
173
  pub webhooks: Option<WebhooksConfig>,
154
174
  pub kvstore: Option<KvStoreConfig>,
175
+ pub sql: Option<SqlConfig>,
155
176
  }
156
177
 
157
178
  impl SdkFullConfig {
@@ -163,6 +184,7 @@ impl SdkFullConfig {
163
184
  streams: None,
164
185
  webhooks: None,
165
186
  kvstore: None,
187
+ sql: None,
166
188
  }
167
189
  }
168
190
 
@@ -189,7 +211,7 @@ impl SdkFullConfig {
189
211
  #[pymethods]
190
212
  impl SdkFullConfig {
191
213
  #[new]
192
- #[pyo3(signature = (api_key, http=None, admin=None, streams=None, webhooks=None, kvstore=None))]
214
+ #[pyo3(signature = (api_key, http=None, admin=None, streams=None, webhooks=None, kvstore=None, sql=None))]
193
215
  pub fn new(
194
216
  api_key: String,
195
217
  http: Option<HttpConfig>,
@@ -197,6 +219,7 @@ impl SdkFullConfig {
197
219
  streams: Option<StreamsConfig>,
198
220
  webhooks: Option<WebhooksConfig>,
199
221
  kvstore: Option<KvStoreConfig>,
222
+ sql: Option<SqlConfig>,
200
223
  ) -> Self {
201
224
  SdkFullConfig {
202
225
  api_key,
@@ -205,6 +228,7 @@ impl SdkFullConfig {
205
228
  streams,
206
229
  webhooks,
207
230
  kvstore,
231
+ sql,
208
232
  }
209
233
  }
210
234
  }
@@ -696,6 +696,7 @@ mod tests {
696
696
  kvstore: Some(KvStoreConfig {
697
697
  base_url: Some(base_url),
698
698
  }),
699
+ sql: None,
699
700
  })
700
701
  .unwrap()
701
702
  }
@@ -2,11 +2,12 @@ pub mod admin;
2
2
  pub mod config;
3
3
  pub mod errors;
4
4
  pub mod kvstore;
5
+ pub mod sql;
5
6
  pub mod streams;
6
7
  pub mod webhooks;
7
8
 
8
9
  pub use config::{
9
- AdminConfig, ClientInfo, HttpConfig, KvStoreConfig, SdkFullConfig, StreamsConfig,
10
+ AdminConfig, ClientInfo, HttpConfig, KvStoreConfig, SdkFullConfig, SqlConfig, StreamsConfig,
10
11
  WebhooksConfig,
11
12
  };
12
13
  pub use kvstore::{
@@ -15,6 +16,10 @@ pub use kvstore::{
15
16
  GetSetsParams, GetSetsResponse, KvSetEntry, KvStoreApiClient, ListContainsItemResponse,
16
17
  UpdateListParams,
17
18
  };
19
+ pub use sql::{
20
+ ChainSchema, ColumnMeta, ColumnSchema, QueryParams, QueryResponse, QueryStatistics,
21
+ SqlApiClient, TableSchema,
22
+ };
18
23
 
19
24
  use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
20
25
  use reqwest::Client as ReqwestClient;
@@ -64,6 +69,7 @@ impl std::fmt::Debug for SdkConfig {
64
69
  .field("streams_base_url", &self.0.streams.base_url)
65
70
  .field("webhooks_base_url", &self.0.webhooks.base_url)
66
71
  .field("kvstore_base_url", &self.0.kvstore.base_url)
72
+ .field("sql_base_url", &self.0.sql.base_url)
67
73
  .finish()
68
74
  }
69
75
  }
@@ -74,6 +80,7 @@ struct SdkConfigInner {
74
80
  streams: streams::ResolvedStreamsConfig,
75
81
  webhooks: webhooks::ResolvedWebhooksConfig,
76
82
  kvstore: kvstore::ResolvedKvStoreConfig,
83
+ sql: sql::ResolvedSqlConfig,
77
84
  }
78
85
 
79
86
  impl SdkConfig {
@@ -159,6 +166,7 @@ impl SdkConfig {
159
166
  streams: streams::ResolvedStreamsConfig::from_config(config.streams.as_ref())?,
160
167
  webhooks: webhooks::ResolvedWebhooksConfig::from_config(config.webhooks.as_ref())?,
161
168
  kvstore: kvstore::ResolvedKvStoreConfig::from_config(config.kvstore.as_ref())?,
169
+ sql: sql::ResolvedSqlConfig::from_config(config.sql.as_ref())?,
162
170
  })))
163
171
  }
164
172
 
@@ -181,6 +189,10 @@ impl SdkConfig {
181
189
  pub(crate) fn kvstore(&self) -> &kvstore::ResolvedKvStoreConfig {
182
190
  &self.0.kvstore
183
191
  }
192
+
193
+ pub(crate) fn sql(&self) -> &sql::ResolvedSqlConfig {
194
+ &self.0.sql
195
+ }
184
196
  }
185
197
 
186
198
  /// Top-level entry point for the Quicknode SDK. Holds sub-clients for each
@@ -196,6 +208,9 @@ pub struct QuicknodeSdk {
196
208
  /// Key-Value Store client: manages sets (single values) and lists
197
209
  /// (ordered collections) under string keys.
198
210
  pub kvstore: kvstore::KvStoreApiClient,
211
+ /// SQL Explorer client: executes SQL queries against indexed blockchain
212
+ /// data and fetches the database schema.
213
+ pub sql: sql::SqlApiClient,
199
214
  }
200
215
 
201
216
  impl QuicknodeSdk {
@@ -216,7 +231,8 @@ impl QuicknodeSdk {
216
231
  admin: admin::AdminApiClient::new(sdk_config.clone()),
217
232
  streams: streams::StreamsApiClient::new(sdk_config.clone()),
218
233
  webhooks: webhooks::WebhooksApiClient::new(sdk_config.clone()),
219
- kvstore: kvstore::KvStoreApiClient::new(sdk_config),
234
+ kvstore: kvstore::KvStoreApiClient::new(sdk_config.clone()),
235
+ sql: sql::SqlApiClient::new(sdk_config),
220
236
  })
221
237
  }
222
238
 
@@ -247,6 +263,7 @@ mod headers_tests {
247
263
  streams: None,
248
264
  webhooks: None,
249
265
  kvstore: None,
266
+ sql: None,
250
267
  }
251
268
  }
252
269
 
@@ -335,6 +352,7 @@ mod headers_tests {
335
352
  streams: None,
336
353
  webhooks: None,
337
354
  kvstore: None,
355
+ sql: None,
338
356
  };
339
357
 
340
358
  let sdk = QuicknodeSdk::new(&cfg).unwrap();