From 755ec5442dcb4c165eba1084d3399b77d4c3abb2 Mon Sep 17 00:00:00 2001 From: Isaac Doidge Date: Wed, 4 Nov 2026 01:49:28 +1000 Subject: [PATCH 1/3] feat(rest): add version() to read the node's version information GeneralRestClient.version() calls GET /rpc/v3/version and returns the response as a dict. Nodes from supra_node_v12.0.0 include a `release` key; the key is absent from the response of an earlier node. Co-Authored-By: Claude Opus 5.5 --- supra_sdk/clients/rest/general.py | 17 +++++++ supra_sdk/clients/rest/rest_types.py | 3 ++ tests/unit/test_version.py | 76 ++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tests/unit/test_version.py diff --git a/supra_sdk/clients/rest/general.py b/supra_sdk/clients/rest/general.py index 8584774..24dd7b5 100644 --- a/supra_sdk/clients/rest/general.py +++ b/supra_sdk/clients/rest/general.py @@ -9,6 +9,7 @@ CHAIN_ID_ENDPOINT, EVENTS_BY_TYPE_ENDPOINT, TABLE_ITEMS_ENDPOINT, + VERSION_ENDPOINT, VIEW_FUNCTION_ENDPOINT, EventsPagination, format_endpoint, @@ -107,3 +108,19 @@ async def events_by_type( params = pagination.to_params() if pagination else {} response = await self.api_client.get(endpoint=endpoint, params=params) return response.json()["data"], response.headers.get("x-supra-cursor", "") + + async def version(self) -> dict[str, Any]: + """Reads the node's version information from `GET /rpc/v3/version`. + + Returns: + dict[str, Any]: The node's response, with these fields: + - version (str): Version of the node's RPC API crate, e.g. '0.5.0'. + - release (str): Release tag of the node, e.g. 'supra_node_v12.0.0'. Sent by nodes from + `supra_node_v12.0.0`; the key is absent from the response of an earlier node. + - branch (str): Git branch the node was built from. May be an empty string. + - commit_hash (str): Git commit the node was built from. + - build_time (str): Time of the build. + - build_env (str): Rust toolchain that built the node. + + """ + return (await self.api_client.get(endpoint=VERSION_ENDPOINT)).json() diff --git a/supra_sdk/clients/rest/rest_types.py b/supra_sdk/clients/rest/rest_types.py index 35bb19d..fdf77c6 100644 --- a/supra_sdk/clients/rest/rest_types.py +++ b/supra_sdk/clients/rest/rest_types.py @@ -173,6 +173,9 @@ def format_endpoint(template: str, **values: Any) -> str: # View function endpoint VIEW_FUNCTION_ENDPOINT = f"{RPC_API_PREFIX}/view" +# Node version endpoint +VERSION_ENDPOINT = f"{RPC_API_PREFIX}/version" + # Table item endpoint TABLE_ITEMS_ENDPOINT = f"{RPC_API_PREFIX}/tables/{{table_handle}}/item" diff --git a/tests/unit/test_version.py b/tests/unit/test_version.py new file mode 100644 index 0000000..bbd9f8d --- /dev/null +++ b/tests/unit/test_version.py @@ -0,0 +1,76 @@ +# Copyright © Supra +# SPDX-License-Identifier: Apache-2.0 + +import unittest +from typing import Any + +import httpx + +from supra_sdk.clients import ApiClient +from supra_sdk.clients.rest.supra_client import SupraClient + +V11_RESPONSE = { + "version": "0.5.0", + "branch": "", + "commit_hash": "f52cb25b7dcdd4ad8b7d7530dbc185ecbeefda02", + "build_time": "2026-11-03 15:04:58 +10:00", + "build_env": "rustc 1.97.1 (8bab26f4f 2026-07-14)", +} + +V12_RESPONSE = { + "version": "0.5.0", + "release": "supra_node_v12.0.0.rc7", + "branch": "", + "commit_hash": "a89b26e61e6f8ff594f3d0435d9050f0b3488a1d", + "build_time": "2026-11-03 15:04:16 +10:00", + "build_env": "rustc 1.99.1 (58cf8f9c3 2026-10-13)", +} + + +async def _client_answering( + body: dict[str, Any], requests: list[httpx.Request] +) -> SupraClient: + """Builds a `SupraClient` whose HTTP transport records requests and answers every one with `body`.""" + + def handle(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, json=body) + + base_url = "http://node.example" + client = SupraClient(base_url) + config = client.api_client.api_client_config + await client.api_client.close() + client.api_client = ApiClient( + base_url, config, transport=httpx.MockTransport(handle) + ) + return client + + +class VersionTests(unittest.IsolatedAsyncioTestCase): + async def test_v12_response_carries_release(self): + requests: list[httpx.Request] = [] + client = await _client_answering(V12_RESPONSE, requests) + try: + result = await client.version() + finally: + await client.api_client.close() + self.assertEqual(result, V12_RESPONSE) + self.assertEqual(result["release"], "supra_node_v12.0.0.rc7") + self.assertEqual(len(requests), 1) + self.assertEqual(requests[0].method, "GET") + self.assertEqual(requests[0].url.path, "/rpc/v3/version") + + async def test_v11_response_has_no_release(self): + requests: list[httpx.Request] = [] + client = await _client_answering(V11_RESPONSE, requests) + try: + result = await client.version() + finally: + await client.api_client.close() + self.assertEqual(result, V11_RESPONSE) + self.assertNotIn("release", result) + self.assertEqual(requests[0].url.path, "/rpc/v3/version") + + +if __name__ == "__main__": + unittest.main() From 0f1b44350818772e45206a972817f745a4de5ff6 Mon Sep 17 00:00:00 2001 From: Isaac Doidge Date: Wed, 4 Nov 2026 01:51:06 +1000 Subject: [PATCH 2/3] docs(rest): align the version() field descriptions with the node's Co-Authored-By: Claude Opus 5.5 --- supra_sdk/clients/rest/general.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/supra_sdk/clients/rest/general.py b/supra_sdk/clients/rest/general.py index 24dd7b5..c5c4857 100644 --- a/supra_sdk/clients/rest/general.py +++ b/supra_sdk/clients/rest/general.py @@ -114,13 +114,18 @@ async def version(self) -> dict[str, Any]: Returns: dict[str, Any]: The node's response, with these fields: - - version (str): Version of the node's RPC API crate, e.g. '0.5.0'. - - release (str): Release tag of the node, e.g. 'supra_node_v12.0.0'. Sent by nodes from - `supra_node_v12.0.0`; the key is absent from the response of an earlier node. - - branch (str): Git branch the node was built from. May be an empty string. - - commit_hash (str): Git commit the node was built from. - - build_time (str): Time of the build. - - build_env (str): Rust toolchain that built the node. + - version (str): Version of the node's Rust packages, e.g. '0.5.0'. It is the same for every + release; use `release` to identify the release. + - release (str): Release tag the node binary was built from, e.g. 'supra_node_v12.0.0', or + 'supra_node_v12.0.0.rc6' for a release candidate. 'unreleased' for a binary built from a commit + that is not a release. The key is absent from the response of a node before + `supra_node_v12.0.0`. + - branch (str): Git branch used for the build. Empty when the build was made from a tag or the + branch was not recorded. + - commit_hash (str): Full git commit hash the binary was built from. Empty when the build did not + record it. + - build_time (str): Build timestamp. + - build_env (str): Rust toolchain used to build the binary. """ return (await self.api_client.get(endpoint=VERSION_ENDPOINT)).json() From 9454cfd3d650adbd4629ce144c9fdc9e17f2a985 Mon Sep 17 00:00:00 2001 From: Isaac Doidge Date: Wed, 4 Nov 2026 01:53:38 +1000 Subject: [PATCH 3/3] docs(rest): state the first node release whose version response has `release` Co-Authored-By: Claude Opus 5.5 --- supra_sdk/clients/rest/general.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supra_sdk/clients/rest/general.py b/supra_sdk/clients/rest/general.py index c5c4857..7747627 100644 --- a/supra_sdk/clients/rest/general.py +++ b/supra_sdk/clients/rest/general.py @@ -117,9 +117,9 @@ async def version(self) -> dict[str, Any]: - version (str): Version of the node's Rust packages, e.g. '0.5.0'. It is the same for every release; use `release` to identify the release. - release (str): Release tag the node binary was built from, e.g. 'supra_node_v12.0.0', or - 'supra_node_v12.0.0.rc6' for a release candidate. 'unreleased' for a binary built from a commit + 'supra_node_v12.0.0.rc7' for a release candidate. 'unreleased' for a binary built from a commit that is not a release. The key is absent from the response of a node before - `supra_node_v12.0.0`. + `supra_node_v12.0.0.rc7`: every v11 release and `supra_node_v12.0.0.rc1` to `rc6`. - branch (str): Git branch used for the build. Empty when the build was made from a tag or the branch was not recorded. - commit_hash (str): Full git commit hash the binary was built from. Empty when the build did not