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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions supra_sdk/clients/rest/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CHAIN_ID_ENDPOINT,
EVENTS_BY_TYPE_ENDPOINT,
TABLE_ITEMS_ENDPOINT,
VERSION_ENDPOINT,
VIEW_FUNCTION_ENDPOINT,
EventsPagination,
format_endpoint,
Expand Down Expand Up @@ -143,3 +144,24 @@ def iter_events_by_type(
pagination or EventsPagination(),
lambda p, cursor: replace(p, start=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 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.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.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
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()
3 changes: 3 additions & 0 deletions supra_sdk/clients/rest/rest_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,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"

Expand Down
76 changes: 76 additions & 0 deletions tests/unit/test_version.py
Original file line number Diff line number Diff line change
@@ -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()
Loading