diff --git a/CHANGELOG.md b/CHANGELOG.md index 319c008..735b296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.0.8 /2025-03-17 + +## What's Changed +* Allows installing on Python 3.13 by @thewhaleking in https://github.com/opentensor/async-substrate-interface/pull/79 +* Support Option types by @ibraheem-opentensor in https://github.com/opentensor/async-substrate-interface/pull/80 + +**Full Changelog**: https://github.com/opentensor/async-substrate-interface/compare/v1.0.7...v1.0.8 + ## 1.0.7 /2025-03-12 ## What's Changed diff --git a/async_substrate_interface/types.py b/async_substrate_interface/types.py index dd60e63..319302a 100644 --- a/async_substrate_interface/types.py +++ b/async_substrate_interface/types.py @@ -631,6 +631,11 @@ def _load_registry_type_map(self, registry): type_id = type_entry["id"] type_def = type_type["def"] type_path = type_type.get("path") + if type_path and type_path[-1] == "Option": + self._handle_option_type( + type_entry, type_id, registry_type_map, type_id_to_name + ) + continue if type_entry.get("params") or type_def.get("variant"): continue # has generics or is Enum if type_path: @@ -686,6 +691,23 @@ def _load_registry_type_map(self, registry): self.registry_type_map = registry_type_map self.type_id_to_name = type_id_to_name + def _handle_option_type( + self, type_entry, type_id, registry_type_map, type_id_to_name + ): + params = type_entry["type"].get("params", []) + if params: + inner_names = [] + for param in params: + inner_id = param["type"] + inner_name = type_id_to_name.get(inner_id, f"Type{inner_id}") + inner_names.append(inner_name) + type_name = f"Option<{', '.join(inner_names)}>" + else: + type_name = "Option" + + registry_type_map[type_name] = type_id + type_id_to_name[type_id] = type_name + def reload_type_registry( self, use_remote_preset: bool = True, auto_discover: bool = True ): @@ -815,10 +837,28 @@ def _encode_scale(self, type_string, value: Any) -> bytes: except KeyError: vec_acct_id = "scale_info::152" + try: + optional_acct_u16 = f"scale_info::{self.registry_type_map['Option<(AccountId32, u16)>']}" + except KeyError: + optional_acct_u16 = "scale_info::573" + if type_string == "scale_info::0": # Is an AccountId # encode string into AccountId ## AccountId is a composite type with one, unnamed field - return bytes.fromhex(ss58_decode(value, SS58_FORMAT)) + return self._encode_account_id(value) + + elif type_string == optional_acct_u16: + if value is None: + return b"\x00" # None + + if not isinstance(value, (list, tuple)) or len(value) != 2: + raise ValueError("Expected tuple of (account_id, u16)") + account_id, u16_value = value + + result = b"\x01" + result += self._encode_account_id(account_id) + result += u16_value.to_bytes(2, "little") + return result elif type_string == vec_acct_id: # Vec if not isinstance(value, (list, tuple)): @@ -833,12 +873,7 @@ def _encode_scale(self, type_string, value: Any) -> bytes: # Encode each AccountId for account in value: - if isinstance(account, bytes): - result += account # Already encoded - else: - result += bytes.fromhex( - ss58_decode(account, SS58_FORMAT) - ) # SS58 string + result += self._encode_account_id(account) return result if isinstance(value, ScaleType): @@ -852,3 +887,16 @@ def _encode_scale(self, type_string, value: Any) -> bytes: encode_by_type_string(type_string, self.runtime.registry, value) ) return result + + def _encode_account_id(self, account) -> bytes: + """Encode an account ID into bytes. + + Args: + account: Either bytes (already encoded) or SS58 string + + Returns: + bytes: The encoded account ID + """ + if isinstance(account, bytes): + return account # Already encoded + return bytes.fromhex(ss58_decode(account, SS58_FORMAT)) # SS58 string diff --git a/pyproject.toml b/pyproject.toml index c5584d8..3fff69a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "async-substrate-interface" -version = "1.0.7" +version = "1.0.8" description = "Asyncio library for interacting with substrate. Mostly API-compatible with py-substrate-interface" readme = "README.md" license = { file = "LICENSE" } @@ -16,7 +16,7 @@ dependencies = [ "xxhash" ] -requires-python = ">=3.9,<3.13" +requires-python = ">=3.9,<3.14" authors = [ { name = "Opentensor Foundation" },