Edit on GitHub

communex.client

   1import gc
   2import hashlib
   3import json
   4import queue
   5import threading
   6from concurrent.futures import Future, ThreadPoolExecutor
   7from contextlib import contextmanager
   8from copy import deepcopy
   9from dataclasses import dataclass
  10from time import sleep
  11from typing import Any, Mapping, TypeVar, cast
  12
  13import websocket
  14from substrateinterface import ExtrinsicReceipt, Keypair, SubstrateInterface
  15from substrateinterface.storage import StorageKey
  16
  17from communex._common import transform_stake_dmap
  18from communex.encryption import bytes_from_hex, encrypt_weights
  19from communex.errors import ChainTransactionError, NetworkQueryError
  20from communex.types import NetworkParams, Ss58Address, SubnetParams
  21
  22# TODO: InsufficientBalanceError, MismatchedLengthError etc
  23
  24MAX_REQUEST_SIZE = 9_000_000
  25
  26
  27@dataclass
  28class ConnectionContainer:
  29    substrate: SubstrateInterface
  30    stop_event: threading.Event
  31    lock: threading.Lock
  32
  33
  34@dataclass
  35class Chunk:
  36    batch_requests: list[tuple[Any, Any]]
  37    prefix_list: list[list[str]]
  38    fun_params: list[tuple[Any, Any, Any, Any, str]]
  39
  40
  41T1 = TypeVar("T1")
  42T2 = TypeVar("T2")
  43
  44
  45def send_heartbeat(
  46    si: SubstrateInterface,
  47    stop: threading.Event,
  48    lock: threading.Lock,
  49):
  50    while not stop.is_set():
  51        # uses substrateinterface wrapper because its very stateful
  52        # and we could mess with something by directly using the websocket
  53        with lock:
  54            _ = si.websocket.pong()  # type: ignore
  55        sleep(11)  # Send heartbeat every 30 seconds
  56
  57
  58def _instantiate_substrateinterface(
  59    url: str, ws_options: dict[str, bool | int], lock: threading.Lock
  60):
  61    ws = websocket.WebSocket()
  62    ws.connect(url)  # type: ignore
  63    stop_event = threading.Event()
  64    si = SubstrateInterface(websocket=ws, ws_options=ws_options)
  65    heartbeat_thread = threading.Thread(
  66        target=send_heartbeat, args=(si, stop_event, lock), daemon=True
  67    )
  68    heartbeat_thread.start()
  69
  70    return ConnectionContainer(
  71        si,
  72        stop_event,
  73        lock,
  74    )
  75
  76
  77class CommuneClient:
  78    """
  79    A client for interacting with Commune network nodes, querying storage,
  80    submitting transactions, etc.
  81
  82    Attributes:
  83        wait_for_finalization: Whether to wait for transaction finalization.
  84
  85    Example:
  86    ```py
  87    client = CommuneClient()
  88    client.query(name='function_name', params=['param1', 'param2'])
  89    ```
  90
  91    Raises:
  92        AssertionError: If the maximum connections value is less than or equal
  93          to zero.
  94    """
  95
  96    wait_for_finalization: bool
  97    _num_connections: int
  98    _connection_queue: queue.Queue[ConnectionContainer]
  99    _ws_options: dict[str, int]
 100    url: str
 101
 102    def __init__(
 103        self,
 104        url: str,
 105        num_connections: int = 1,
 106        wait_for_finalization: bool = False,
 107        timeout: int | None = None,
 108    ):
 109        """
 110        Args:
 111            url: The URL of the network node to connect to.
 112            num_connections: The number of websocket connections to be opened.
 113        """
 114        assert num_connections > 0
 115        self._num_connections = num_connections
 116        self.wait_for_finalization = wait_for_finalization
 117        self._connection_queue = queue.Queue(num_connections)
 118        self.url = url
 119
 120        for _ in range(num_connections):
 121            ws_options: dict[str, int] = {}
 122            if timeout is not None:
 123                ws_options["timeout"] = timeout
 124            self._ws_options = ws_options
 125            self._connection_queue.put(
 126                _instantiate_substrateinterface(
 127                    url, ws_options, threading.Lock()
 128                )
 129            )
 130
 131    @property
 132    def connections(self) -> int:
 133        """
 134        Gets the maximum allowed number of simultaneous connections to the
 135        network node.
 136        """
 137        return self._num_connections
 138
 139    @contextmanager
 140    def get_conn(self, timeout: float | None = None, init: bool = False):
 141        """
 142        Context manager to get a connection from the pool.
 143
 144        Tries to get a connection from the pool queue. If the queue is empty,
 145        it blocks for `timeout` seconds until a connection is available. If
 146        `timeout` is None, it blocks indefinitely.
 147
 148        Args:
 149            timeout: The maximum time in seconds to wait for a connection.
 150
 151        Yields:
 152            The connection object from the pool.
 153
 154        Raises:
 155            QueueEmptyError: If no connection is available within the timeout
 156              period.
 157        """
 158        conn = self._connection_queue.get(timeout=timeout)
 159        if init:
 160            conn.substrate.init_runtime()  # type: ignore
 161        try:
 162            gc.collect()
 163            if conn.substrate.websocket and conn.substrate.websocket.connected:  # type: ignore
 164                with conn.lock:
 165                    yield conn.substrate
 166            else:
 167                # reconnects
 168                conn.stop_event.set()
 169                conn = _instantiate_substrateinterface(
 170                    self.url, self._ws_options, threading.Lock()
 171                )
 172                with conn.lock:
 173                    yield conn.substrate
 174        finally:
 175            self._connection_queue.put(conn)
 176
 177    def _get_storage_keys(
 178        self,
 179        storage: str,
 180        queries: list[tuple[str, list[Any]]],
 181        block_hash: str | None,
 182    ):
 183        send: list[tuple[str, list[Any]]] = []
 184        prefix_list: list[Any] = []
 185
 186        key_idx = 0
 187        with self.get_conn(init=True) as substrate:
 188            for function, params in queries:
 189                storage_key = StorageKey.create_from_storage_function(  # type: ignore
 190                    storage,
 191                    function,
 192                    params,
 193                    runtime_config=substrate.runtime_config,  # type: ignore
 194                    metadata=substrate.metadata,  # type: ignore
 195                )
 196
 197                prefix = storage_key.to_hex()
 198                prefix_list.append(prefix)
 199                send.append(("state_getKeys", [prefix, block_hash]))
 200                key_idx += 1
 201        return send, prefix_list
 202
 203    def _get_lists(
 204        self,
 205        storage_module: str,
 206        queries: list[tuple[str, list[Any]]],
 207        substrate: SubstrateInterface,
 208    ) -> list[tuple[Any, Any, Any, Any, str]]:
 209        """
 210        Generates a list of tuples containing parameters for each storage function based on the given functions and substrate interface.
 211
 212        Args:
 213            functions (dict[str, list[query_call]]): A dictionary where keys are storage module names and values are lists of tuples.
 214                Each tuple consists of a storage function name and its parameters.
 215            substrate: An instance of the SubstrateInterface class used to interact with the substrate.
 216
 217        Returns:
 218            A list of tuples in the format `(value_type, param_types, key_hashers, params, storage_function)` for each storage function in the given functions.
 219
 220        Example:
 221            >>> _get_lists(
 222                    functions={'storage_module': [('storage_function', ['param1', 'param2'])]},
 223                    substrate=substrate_instance
 224                )
 225            [('value_type', 'param_types', 'key_hashers', ['param1', 'param2'], 'storage_function'), ...]
 226        """
 227
 228        function_parameters: list[tuple[Any, Any, Any, Any, str]] = []
 229
 230        metadata_pallet = substrate.metadata.get_metadata_pallet(  # type: ignore
 231            storage_module
 232        )
 233        for storage_function, params in queries:
 234            storage_item = metadata_pallet.get_storage_function(  # type: ignore
 235                storage_function
 236            )
 237
 238            value_type = storage_item.get_value_type_string()  # type: ignore
 239            param_types = storage_item.get_params_type_string()  # type: ignore
 240            key_hashers = storage_item.get_param_hashers()  # type: ignore
 241            function_parameters.append(
 242                (
 243                    value_type,
 244                    param_types,
 245                    key_hashers,
 246                    params,
 247                    storage_function,
 248                )  # type: ignore
 249            )
 250        return function_parameters
 251
 252    def _send_batch(
 253        self,
 254        batch_payload: list[Any],
 255        request_ids: list[int],
 256        extract_result: bool = True,
 257    ):
 258        """
 259        Sends a batch of requests to the substrate and collects the results.
 260
 261        Args:
 262            substrate: An instance of the substrate interface.
 263            batch_payload: The payload of the batch request.
 264            request_ids: A list of request IDs for tracking responses.
 265            results: A list to store the results of the requests.
 266            extract_result: Whether to extract the result from the response.
 267
 268        Raises:
 269            NetworkQueryError: If there is an `error` in the response message.
 270
 271        Note:
 272            No explicit return value as results are appended to the provided 'results' list.
 273        """
 274        results: list[str | dict[Any, Any]] = []
 275        with self.get_conn(init=True) as substrate:
 276            try:
 277                substrate.websocket.send(  #  type: ignore
 278                    json.dumps(batch_payload)
 279                )
 280            except NetworkQueryError:
 281                pass
 282            while len(results) < len(request_ids):
 283                received_messages = json.loads(
 284                    substrate.websocket.recv()  # type: ignore
 285                )
 286                if isinstance(received_messages, dict):
 287                    received_messages: list[dict[Any, Any]] = [
 288                        received_messages
 289                    ]
 290
 291                for message in received_messages:
 292                    if message.get("id") in request_ids:
 293                        if extract_result:
 294                            try:
 295                                results.append(message["result"])
 296                            except Exception:
 297                                raise (
 298                                    RuntimeError(
 299                                        f"Error extracting result from message: {message}"
 300                                    )
 301                                )
 302                        else:
 303                            results.append(message)
 304                    if "error" in message:
 305                        raise NetworkQueryError(message["error"])
 306
 307            return results
 308
 309    def _make_request_smaller(
 310        self,
 311        batch_request: list[tuple[T1, T2]],
 312        prefix_list: list[list[str]],
 313        fun_params: list[tuple[Any, Any, Any, Any, str]],
 314    ) -> tuple[list[list[tuple[T1, T2]]], list[Chunk]]:
 315        """
 316        Splits a batch of requests into smaller batches, each not exceeding the specified maximum size.
 317
 318        Args:
 319            batch_request: A list of requests to be sent in a batch.
 320            max_size: Maximum size of each batch in bytes.
 321
 322        Returns:
 323            A list of smaller request batches.
 324
 325        Example:
 326            >>> _make_request_smaller(batch_request=[('method1', 'params1'), ('method2', 'params2')], max_size=1000)
 327            [[('method1', 'params1')], [('method2', 'params2')]]
 328        """
 329        assert len(prefix_list) == len(fun_params) == len(batch_request)
 330
 331        def estimate_size(request: tuple[T1, T2]):
 332            """Convert the batch request to a string and measure its length"""
 333            return len(json.dumps(request))
 334
 335        # Initialize variables
 336        result: list[list[tuple[T1, T2]]] = []
 337        current_batch = []
 338        current_prefix_batch = []
 339        current_params_batch = []
 340        current_size = 0
 341
 342        chunk_list: list[Chunk] = []
 343
 344        # Iterate through each request in the batch
 345        for request, prefix, params in zip(
 346            batch_request, prefix_list, fun_params
 347        ):
 348            request_size = estimate_size(request)
 349
 350            # Check if adding this request exceeds the max size
 351            if current_size + request_size > MAX_REQUEST_SIZE:
 352                # If so, start a new batch
 353
 354                # Essentiatly checks that it's not the first iteration
 355                if current_batch:
 356                    chunk = Chunk(
 357                        current_batch,
 358                        current_prefix_batch,
 359                        current_params_batch,
 360                    )
 361                    chunk_list.append(chunk)
 362                    result.append(current_batch)
 363
 364                current_batch = [request]
 365                current_prefix_batch = [prefix]
 366                current_params_batch = [params]
 367                current_size = request_size
 368            else:
 369                # Otherwise, add to the current batch
 370                current_batch.append(request)
 371                current_size += request_size
 372                current_prefix_batch.append(prefix)
 373                current_params_batch.append(params)
 374
 375        # Add the last batch if it's not empty
 376        if current_batch:
 377            result.append(current_batch)
 378            chunk = Chunk(
 379                current_batch, current_prefix_batch, current_params_batch
 380            )
 381            chunk_list.append(chunk)
 382
 383        return result, chunk_list
 384
 385    def _are_changes_equal(self, change_a: Any, change_b: Any):
 386        for (a, b), (c, d) in zip(change_a, change_b):
 387            if a != c or b != d:
 388                return False
 389
 390    def _rpc_request_batch(
 391        self,
 392        batch_requests: list[tuple[str, list[Any]]],
 393        extract_result: bool = True,
 394    ) -> list[str]:
 395        """
 396        Sends batch requests to the substrate node using multiple threads and collects the results.
 397
 398        Args:
 399            substrate: An instance of the substrate interface.
 400            batch_requests : A list of requests to be sent in batches.
 401            max_size: Maximum size of each batch in bytes.
 402            extract_result: Whether to extract the result from the response message.
 403
 404        Returns:
 405            A list of results from the batch requests.
 406
 407        Example:
 408            >>> _rpc_request_batch(substrate_instance, [('method1', ['param1']), ('method2', ['param2'])])
 409            ['result1', 'result2', ...]
 410        """
 411
 412        chunk_results: list[Any] = []
 413        # smaller_requests = self._make_request_smaller(batch_requests)
 414        request_id = 0
 415        with ThreadPoolExecutor() as executor:
 416            futures: list[Future[list[str | dict[Any, Any]]]] = []
 417            for chunk in [batch_requests]:
 418                request_ids: list[int] = []
 419                batch_payload: list[Any] = []
 420                for method, params in chunk:
 421                    request_id += 1
 422                    request_ids.append(request_id)
 423                    batch_payload.append(
 424                        {
 425                            "jsonrpc": "2.0",
 426                            "method": method,
 427                            "params": params,
 428                            "id": request_id,
 429                        }
 430                    )
 431
 432                futures.append(
 433                    executor.submit(
 434                        self._send_batch,
 435                        batch_payload=batch_payload,
 436                        request_ids=request_ids,
 437                        extract_result=extract_result,
 438                    )
 439                )
 440            for future in futures:
 441                resul = future.result()
 442                chunk_results.append(resul)
 443        return chunk_results
 444
 445    def _rpc_request_batch_chunked(
 446        self, chunk_requests: list[Chunk], extract_result: bool = True
 447    ):
 448        """
 449        Sends batch requests to the substrate node using multiple threads and collects the results.
 450
 451        Args:
 452            substrate: An instance of the substrate interface.
 453            batch_requests : A list of requests to be sent in batches.
 454            max_size: Maximum size of each batch in bytes.
 455            extract_result: Whether to extract the result from the response message.
 456
 457        Returns:
 458            A list of results from the batch requests.
 459
 460        Example:
 461            >>> _rpc_request_batch(substrate_instance, [('method1', ['param1']), ('method2', ['param2'])])
 462            ['result1', 'result2', ...]
 463        """
 464
 465        def split_chunks(
 466            chunk: Chunk, chunk_info: list[Chunk], chunk_info_idx: int
 467        ):
 468            manhattam_chunks: list[tuple[Any, Any]] = []
 469            mutaded_chunk_info = deepcopy(chunk_info)
 470            max_n_keys = 35000
 471            for query in chunk.batch_requests:
 472                result_keys = query[1][0]
 473                keys_amount = len(result_keys)
 474                if keys_amount > max_n_keys:
 475                    mutaded_chunk_info.pop(chunk_info_idx)
 476                    for i in range(0, keys_amount, max_n_keys):
 477                        new_chunk = deepcopy(chunk)
 478                        splitted_keys = result_keys[i : i + max_n_keys]
 479                        splitted_query = deepcopy(query)
 480                        splitted_query[1][0] = splitted_keys
 481                        new_chunk.batch_requests = [splitted_query]
 482                        manhattam_chunks.append(splitted_query)
 483                        mutaded_chunk_info.insert(chunk_info_idx, new_chunk)
 484                else:
 485                    manhattam_chunks.append(query)
 486            return manhattam_chunks, mutaded_chunk_info
 487
 488        assert len(chunk_requests) > 0
 489        mutated_chunk_info: list[Chunk] = []
 490        chunk_results: list[Any] = []
 491        # smaller_requests = self._make_request_smaller(batch_requests)
 492        request_id = 0
 493
 494        with ThreadPoolExecutor() as executor:
 495            futures: list[Future[list[str | dict[Any, Any]]]] = []
 496            for idx, macro_chunk in enumerate(chunk_requests):
 497                _, mutated_chunk_info = split_chunks(
 498                    macro_chunk, chunk_requests, idx
 499                )
 500            for chunk in mutated_chunk_info:
 501                request_ids: list[int] = []
 502                batch_payload: list[Any] = []
 503                for method, params in chunk.batch_requests:
 504                    # for method, params in micro_chunk:
 505                    request_id += 1
 506                    request_ids.append(request_id)
 507                    batch_payload.append(
 508                        {
 509                            "jsonrpc": "2.0",
 510                            "method": method,
 511                            "params": params,
 512                            "id": request_id,
 513                        }
 514                    )
 515                futures.append(
 516                    executor.submit(
 517                        self._send_batch,
 518                        batch_payload=batch_payload,
 519                        request_ids=request_ids,
 520                        extract_result=extract_result,
 521                    )
 522                )
 523            for future in futures:
 524                resul = future.result()
 525                chunk_results.append(resul)
 526        return chunk_results, mutated_chunk_info
 527
 528    def _decode_response(
 529        self,
 530        response: list[str],
 531        function_parameters: list[tuple[Any, Any, Any, Any, str]],
 532        prefix_list: list[Any],
 533        block_hash: str,
 534    ) -> dict[str, dict[Any, Any]]:
 535        """
 536        Decodes a response from the substrate interface and organizes the data into a dictionary.
 537
 538        Args:
 539            response: A list of encoded responses from a substrate query.
 540            function_parameters: A list of tuples containing the parameters for each storage function.
 541            last_keys: A list of the last keys used in the substrate query.
 542            prefix_list: A list of prefixes used in the substrate query.
 543            substrate: An instance of the SubstrateInterface class.
 544            block_hash: The hash of the block to be queried.
 545
 546        Returns:
 547            A dictionary where each key is a storage function name and the value is another dictionary.
 548            This inner dictionary's key is the decoded key from the response and the value is the corresponding decoded value.
 549
 550        Raises:
 551            ValueError: If an unsupported hash type is encountered in the `concat_hash_len` function.
 552
 553        Example:
 554            >>> _decode_response(
 555                    response=[...],
 556                    function_parameters=[...],
 557                    last_keys=[...],
 558                    prefix_list=[...],
 559                    substrate=substrate_instance,
 560                    block_hash="0x123..."
 561                )
 562            {'storage_function_name': {decoded_key: decoded_value, ...}, ...}
 563        """
 564
 565        def get_item_key_value(
 566            item_key: tuple[Any, ...] | Any,
 567        ) -> tuple[Any, ...] | Any:
 568            if isinstance(item_key, tuple):
 569                return tuple(k.value for k in item_key)  # type: ignore
 570            return item_key.value
 571
 572        def concat_hash_len(key_hasher: str) -> int:
 573            """
 574            Determines the length of the hash based on the given key hasher type.
 575
 576            Args:
 577                key_hasher: The type of key hasher.
 578
 579            Returns:
 580                The length of the hash corresponding to the given key hasher type.
 581
 582            Raises:
 583                ValueError: If the key hasher type is not supported.
 584
 585            Example:
 586                >>> concat_hash_len("Blake2_128Concat")
 587                16
 588            """
 589
 590            if key_hasher == "Blake2_128Concat":
 591                return 16
 592            elif key_hasher == "Twox64Concat":
 593                return 8
 594            elif key_hasher == "Identity":
 595                return 0
 596            else:
 597                raise ValueError("Unsupported hash type")
 598
 599        assert len(response) == len(function_parameters) == len(prefix_list)
 600        result_dict: dict[str, dict[Any, Any]] = {}
 601        for res, fun_params_tuple, prefix in zip(
 602            response, function_parameters, prefix_list
 603        ):
 604            if not res:
 605                continue
 606            res = res[0]
 607            changes = res["changes"]  # type: ignore
 608            value_type, param_types, key_hashers, params, storage_function = (
 609                fun_params_tuple
 610            )
 611            with self.get_conn(init=True) as substrate:
 612                for item in changes:
 613                    # Determine type string
 614                    key_type_string: list[Any] = []
 615                    for n in range(len(params), len(param_types)):
 616                        key_type_string.append(
 617                            f"[u8; {concat_hash_len(key_hashers[n])}]"
 618                        )
 619                        key_type_string.append(param_types[n])
 620
 621                    item_key_obj = substrate.decode_scale(  # type: ignore
 622                        type_string=f"({', '.join(key_type_string)})",
 623                        scale_bytes="0x" + item[0][len(prefix) :],
 624                        return_scale_obj=True,
 625                        block_hash=block_hash,
 626                    )
 627                    # strip key_hashers to use as item key
 628                    if len(param_types) - len(params) == 1:
 629                        item_key = item_key_obj.value_object[1]  # type: ignore
 630                    else:
 631                        item_key = tuple(  # type: ignore
 632                            item_key_obj.value_object[key + 1]  # type: ignore
 633                            for key in range(  # type: ignore
 634                                len(params), len(param_types) + 1, 2
 635                            )
 636                        )
 637
 638                    item_value = substrate.decode_scale(  # type: ignore
 639                        type_string=value_type,
 640                        scale_bytes=item[1],
 641                        return_scale_obj=True,
 642                        block_hash=block_hash,
 643                    )
 644                    result_dict.setdefault(storage_function, {})
 645                    key = get_item_key_value(item_key)  # type: ignore
 646                    result_dict[storage_function][key] = item_value.value  # type: ignore
 647
 648        return result_dict
 649
 650    def query_batch(
 651        self, functions: dict[str, list[tuple[str, list[Any]]]]
 652    ) -> dict[str, str]:
 653        """
 654        Executes batch queries on a substrate and returns results in a dictionary format.
 655
 656        Args:
 657            substrate: An instance of SubstrateInterface to interact with the substrate.
 658            functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls (function name and parameters).
 659
 660        Returns:
 661            A dictionary where keys are storage function names and values are the query results.
 662
 663        Raises:
 664            Exception: If no result is found from the batch queries.
 665
 666        Example:
 667            >>> query_batch(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
 668            {'function_name': 'query_result', ...}
 669        """
 670
 671        result: dict[str, str] = {}
 672        if not functions:
 673            raise Exception("No result")
 674        with self.get_conn(init=True) as substrate:
 675            for module, queries in functions.items():
 676                storage_keys: list[Any] = []
 677                for fn, params in queries:
 678                    storage_function = substrate.create_storage_key(  # type: ignore
 679                        pallet=module, storage_function=fn, params=params
 680                    )
 681                    storage_keys.append(storage_function)
 682
 683                block_hash = substrate.get_block_hash()
 684                responses: list[Any] = substrate.query_multi(  # type: ignore
 685                    storage_keys=storage_keys, block_hash=block_hash
 686                )
 687
 688                for item in responses:
 689                    fun = item[0]
 690                    query = item[1]
 691                    storage_fun = fun.storage_function
 692                    result[storage_fun] = query.value
 693
 694        return result
 695
 696    def query_batch_map(
 697        self,
 698        functions: dict[str, list[tuple[str, list[Any]]]],
 699        block_hash: str | None = None,
 700    ) -> dict[str, dict[Any, Any]]:
 701        """
 702        Queries multiple storage functions using a map batch approach and returns the combined result.
 703
 704        Args:
 705            substrate: An instance of SubstrateInterface for substrate interaction.
 706            functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls.
 707
 708        Returns:
 709            The combined result of the map batch query.
 710
 711        Example:
 712            >>> query_batch_map(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
 713            # Returns the combined result of the map batch query
 714        """
 715        multi_result: dict[str, dict[Any, Any]] = {}
 716
 717        def recursive_update(
 718            d: dict[str, dict[T1, T2] | dict[str, Any]],
 719            u: Mapping[str, dict[Any, Any] | str],
 720        ) -> dict[str, dict[T1, T2]]:
 721            for k, v in u.items():
 722                if isinstance(v, dict):
 723                    d[k] = recursive_update(d.get(k, {}), v)  # type: ignore
 724                else:
 725                    d[k] = v  # type: ignore
 726            return d  # type: ignore
 727
 728        def get_page():
 729            send, prefix_list = self._get_storage_keys(
 730                storage, queries, block_hash
 731            )
 732            with self.get_conn(init=True) as substrate:
 733                function_parameters = self._get_lists(
 734                    storage, queries, substrate
 735                )
 736            responses = self._rpc_request_batch(send)
 737            # assumption because send is just the storage_function keys
 738            # so it should always be really small regardless of the amount of queries
 739            assert len(responses) == 1
 740            res = responses[0]
 741            built_payload: list[tuple[str, list[Any]]] = []
 742            for result_keys in res:
 743                built_payload.append(
 744                    ("state_queryStorageAt", [result_keys, block_hash])
 745                )
 746            _, chunks_info = self._make_request_smaller(
 747                built_payload, prefix_list, function_parameters
 748            )
 749            chunks_response, chunks_info = self._rpc_request_batch_chunked(
 750                chunks_info
 751            )
 752            return chunks_response, chunks_info
 753
 754        if not block_hash:
 755            with self.get_conn(init=True) as substrate:
 756                block_hash = substrate.get_block_hash()
 757        for storage, queries in functions.items():
 758            chunks, chunks_info = get_page()
 759            # if this doesn't happen something is wrong on the code
 760            # and we won't be able to decode the data properly
 761            assert len(chunks) == len(chunks_info)
 762            for chunk_info, response in zip(chunks_info, chunks):
 763                storage_result = self._decode_response(
 764                    response,
 765                    chunk_info.fun_params,
 766                    chunk_info.prefix_list,
 767                    block_hash,
 768                )
 769                multi_result = recursive_update(multi_result, storage_result)
 770
 771        return multi_result
 772
 773    def query(
 774        self,
 775        name: str,
 776        params: list[Any] = [],
 777        module: str = "SubspaceModule",
 778        block_hash: str | None = None,
 779    ) -> Any:
 780        """
 781        Queries a storage function on the network.
 782
 783        Sends a query to the network and retrieves data from a
 784        specified storage function.
 785
 786        Args:
 787            name: The name of the storage function to query.
 788            params: The parameters to pass to the storage function.
 789            module: The module where the storage function is located.
 790
 791        Returns:
 792            The result of the query from the network.
 793
 794        Raises:
 795            NetworkQueryError: If the query fails or is invalid.
 796        """
 797
 798        result = self.query_batch({module: [(name, params)]})
 799
 800        return result[name]
 801
 802    def query_map(
 803        self,
 804        name: str,
 805        params: list[Any] = [],
 806        module: str = "SubspaceModule",
 807        extract_value: bool = True,
 808        block_hash: str | None = None,
 809    ) -> dict[Any, Any]:
 810        """
 811        Queries a storage map from a network node.
 812
 813        Args:
 814            name: The name of the storage map to query.
 815            params: A list of parameters for the query.
 816            module: The module in which the storage map is located.
 817
 818        Returns:
 819            A dictionary representing the key-value pairs
 820              retrieved from the storage map.
 821
 822        Raises:
 823            QueryError: If the query to the network fails or is invalid.
 824        """
 825
 826        result = self.query_batch_map({module: [(name, params)]}, block_hash)
 827
 828        if extract_value:
 829            return {k.value: v.value for k, v in result}  # type: ignore
 830
 831        return result
 832
 833    def compose_call(
 834        self,
 835        fn: str,
 836        params: dict[str, Any],
 837        key: Keypair | None,
 838        module: str = "SubspaceModule",
 839        wait_for_inclusion: bool = True,
 840        wait_for_finalization: bool | None = None,
 841        sudo: bool = False,
 842        unsigned: bool = False,
 843    ) -> ExtrinsicReceipt:
 844        """
 845        Composes and submits a call to the network node.
 846
 847        Composes and signs a call with the provided keypair, and submits it to
 848        the network. The call can be a standard extrinsic or a sudo extrinsic if
 849        elevated permissions are required. The method can optionally wait for
 850        the call's inclusion in a block and/or its finalization.
 851
 852        Args:
 853            fn: The function name to call on the network.
 854            params: A dictionary of parameters for the call.
 855            key: The keypair for signing the extrinsic.
 856            module: The module containing the function.
 857            wait_for_inclusion: Wait for the call's inclusion in a block.
 858            wait_for_finalization: Wait for the transaction's finalization.
 859            sudo: Execute the call as a sudo (superuser) operation.
 860
 861        Returns:
 862            The receipt of the submitted extrinsic, if
 863              `wait_for_inclusion` is True. Otherwise, returns a string
 864              identifier of the extrinsic.
 865
 866        Raises:
 867            ChainTransactionError: If the transaction fails.
 868        """
 869
 870        if key is None and not unsigned:
 871            raise ValueError("Key must be provided for signed extrinsics.")
 872
 873        with self.get_conn() as substrate:
 874            if wait_for_finalization is None:
 875                wait_for_finalization = self.wait_for_finalization
 876
 877            call = substrate.compose_call(  # type: ignore
 878                call_module=module, call_function=fn, call_params=params
 879            )
 880            if sudo:
 881                call = substrate.compose_call(  # type: ignore
 882                    call_module="Sudo",
 883                    call_function="sudo",
 884                    call_params={
 885                        "call": call.value,  # type: ignore
 886                    },
 887                )
 888
 889            if not unsigned:
 890                assert key is not None
 891                extrinsic = substrate.create_signed_extrinsic(  # type: ignore
 892                    call=call,
 893                    keypair=key,
 894                )
 895            else:
 896                extrinsic = substrate.create_unsigned_extrinsic(call=call)  # type: ignore
 897
 898            response = substrate.submit_extrinsic(
 899                extrinsic=extrinsic,
 900                wait_for_inclusion=wait_for_inclusion,
 901                wait_for_finalization=wait_for_finalization,
 902            )
 903        if wait_for_inclusion:
 904            if not response.is_success:
 905                raise ChainTransactionError(
 906                    response.error_message,  # type: ignore
 907                    response,  # type: ignore
 908                )
 909
 910        return response
 911
 912    def compose_call_multisig(
 913        self,
 914        fn: str,
 915        params: dict[str, Any],
 916        key: Keypair,
 917        signatories: list[Ss58Address],
 918        threshold: int,
 919        module: str = "SubspaceModule",
 920        wait_for_inclusion: bool = True,
 921        wait_for_finalization: bool | None = None,
 922        sudo: bool = False,
 923        era: dict[str, int] | None = None,
 924    ) -> ExtrinsicReceipt:
 925        """
 926        Composes and submits a multisignature call to the network node.
 927
 928        This method allows the composition and submission of a call that
 929        requires multiple signatures for execution, known as a multisignature
 930        call. It supports specifying signatories, a threshold of signatures for
 931        the call's execution, and an optional era for the call's mortality. The
 932        call can be a standard extrinsic, a sudo extrinsic for elevated
 933        permissions, or a multisig extrinsic if multiple signatures are
 934        required. Optionally, the method can wait for the call's inclusion in a
 935        block and/or its finalization. Make sure to pass all keys,
 936        that are part of the multisignature.
 937
 938        Args:
 939            fn: The function name to call on the network. params: A dictionary
 940            of parameters for the call. key: The keypair for signing the
 941            extrinsic. signatories: List of SS58 addresses of the signatories.
 942            Include ALL KEYS that are part of the multisig. threshold: The
 943            minimum number of signatories required to execute the extrinsic.
 944            module: The module containing the function to call.
 945            wait_for_inclusion: Whether to wait for the call's inclusion in a
 946            block. wait_for_finalization: Whether to wait for the transaction's
 947            finalization. sudo: Execute the call as a sudo (superuser)
 948            operation. era: Specifies the call's mortality in terms of blocks in
 949            the format
 950                {'period': amount_blocks}. If omitted, the extrinsic is
 951                immortal.
 952
 953        Returns:
 954            The receipt of the submitted extrinsic if `wait_for_inclusion` is
 955            True. Otherwise, returns a string identifier of the extrinsic.
 956
 957        Raises:
 958            ChainTransactionError: If the transaction fails.
 959        """
 960
 961        # getting the call ready
 962        with self.get_conn() as substrate:
 963            if wait_for_finalization is None:
 964                wait_for_finalization = self.wait_for_finalization
 965
 966            # prepares the `GenericCall` object
 967            call = substrate.compose_call(  # type: ignore
 968                call_module=module, call_function=fn, call_params=params
 969            )
 970            if sudo:
 971                call = substrate.compose_call(  # type: ignore
 972                    call_module="Sudo",
 973                    call_function="sudo",
 974                    call_params={
 975                        "call": call.value,  # type: ignore
 976                    },
 977                )
 978
 979            # modify the rpc methods at runtime, to allow for correct payment
 980            # fee calculation parity has a bug in this version,
 981            # where the method has to be removed
 982            rpc_methods = substrate.config.get("rpc_methods")  # type: ignore
 983
 984            if "state_call" in rpc_methods:  # type: ignore
 985                rpc_methods.remove("state_call")  # type: ignore
 986
 987            # create the multisig account
 988            multisig_acc = substrate.generate_multisig_account(  # type: ignore
 989                signatories, threshold
 990            )
 991
 992            # send the multisig extrinsic
 993            extrinsic = substrate.create_multisig_extrinsic(  # type: ignore
 994                call=call,  # type: ignore
 995                keypair=key,
 996                multisig_account=multisig_acc,  # type: ignore
 997                era=era,  # type: ignore
 998            )  # type: ignore
 999
1000            response = substrate.submit_extrinsic(
1001                extrinsic=extrinsic,
1002                wait_for_inclusion=wait_for_inclusion,
1003                wait_for_finalization=wait_for_finalization,
1004            )
1005
1006        if wait_for_inclusion:
1007            if not response.is_success:
1008                raise ChainTransactionError(
1009                    response.error_message,  # type: ignore
1010                    response,  # type: ignore
1011                )
1012
1013        return response
1014
1015    def transfer(
1016        self,
1017        key: Keypair,
1018        amount: int,
1019        dest: Ss58Address,
1020    ) -> ExtrinsicReceipt:
1021        """
1022        Transfers a specified amount of tokens from the signer's account to the
1023        specified account.
1024
1025        Args:
1026            key: The keypair associated with the sender's account.
1027            amount: The amount to transfer, in nanotokens.
1028            dest: The SS58 address of the recipient.
1029
1030        Returns:
1031            A receipt of the transaction.
1032
1033        Raises:
1034            InsufficientBalanceError: If the sender's account does not have
1035              enough balance.
1036            ChainTransactionError: If the transaction fails.
1037        """
1038
1039        params = {"dest": dest, "value": amount}
1040
1041        return self.compose_call(
1042            module="Balances", fn="transfer_keep_alive", params=params, key=key
1043        )
1044
1045    def transfer_multiple(
1046        self,
1047        key: Keypair,
1048        destinations: list[Ss58Address],
1049        amounts: list[int],
1050        netuid: str | int = 0,
1051    ) -> ExtrinsicReceipt:
1052        """
1053        Transfers specified amounts of tokens from the signer's account to
1054        multiple target accounts.
1055
1056        The `destinations` and `amounts` lists must be of the same length.
1057
1058        Args:
1059            key: The keypair associated with the sender's account.
1060            destinations: A list of SS58 addresses of the recipients.
1061            amounts: Amount to transfer to each recipient, in nanotokens.
1062            netuid: The network identifier.
1063
1064        Returns:
1065            A receipt of the transaction.
1066
1067        Raises:
1068            InsufficientBalanceError: If the sender's account does not have
1069              enough balance for all transfers.
1070            ChainTransactionError: If the transaction fails.
1071        """
1072
1073        assert len(destinations) == len(amounts)
1074
1075        # extract existential deposit from amounts
1076        existential_deposit = self.get_existential_deposit()
1077        amounts = [a - existential_deposit for a in amounts]
1078
1079        params = {
1080            "netuid": netuid,
1081            "destinations": destinations,
1082            "amounts": amounts,
1083        }
1084
1085        return self.compose_call(
1086            module="SubspaceModule",
1087            fn="transfer_multiple",
1088            params=params,
1089            key=key,
1090        )
1091
1092    def stake(
1093        self,
1094        key: Keypair,
1095        amount: int,
1096        dest: Ss58Address,
1097    ) -> ExtrinsicReceipt:
1098        """
1099        Stakes the specified amount of tokens to a module key address.
1100
1101        Args:
1102            key: The keypair associated with the staker's account.
1103            amount: The amount of tokens to stake, in nanotokens.
1104            dest: The SS58 address of the module key to stake to.
1105            netuid: The network identifier.
1106
1107        Returns:
1108            A receipt of the staking transaction.
1109
1110        Raises:
1111            InsufficientBalanceError: If the staker's account does not have
1112              enough balance.
1113            ChainTransactionError: If the transaction fails.
1114        """
1115
1116        params = {"amount": amount, "module_key": dest}
1117
1118        return self.compose_call(fn="add_stake", params=params, key=key)
1119
1120    def unstake(
1121        self,
1122        key: Keypair,
1123        amount: int,
1124        dest: Ss58Address,
1125    ) -> ExtrinsicReceipt:
1126        """
1127        Unstakes the specified amount of tokens from a module key address.
1128
1129        Args:
1130            key: The keypair associated with the unstaker's account.
1131            amount: The amount of tokens to unstake, in nanotokens.
1132            dest: The SS58 address of the module key to unstake from.
1133            netuid: The network identifier.
1134
1135        Returns:
1136            A receipt of the unstaking transaction.
1137
1138        Raises:
1139            InsufficientStakeError: If the staked key does not have enough
1140              staked tokens by the signer key.
1141            ChainTransactionError: If the transaction fails.
1142        """
1143
1144        params = {"amount": amount, "module_key": dest}
1145        return self.compose_call(fn="remove_stake", params=params, key=key)
1146
1147    def update_module(
1148        self,
1149        key: Keypair,
1150        name: str,
1151        address: str,
1152        metadata: str | None = None,
1153        delegation_fee: int = 20,
1154        weight_setting_delegation_fee: int | None = None,
1155        netuid: int = 0,
1156    ) -> ExtrinsicReceipt:
1157        """
1158        Updates the parameters of a registered module.
1159
1160        The delegation fee must be an integer between 0 and 100.
1161
1162        Args:
1163            key: The keypair associated with the module's account.
1164            name: The new name for the module. If None, the name is not updated.
1165            address: The new address for the module.
1166                If None, the address is not updated.
1167            delegation_fee: The new delegation fee for the module,
1168                between 0 and 100.
1169            netuid: The network identifier.
1170
1171        Returns:
1172            A receipt of the module update transaction.
1173
1174        Raises:
1175            InvalidParameterError: If the provided parameters are invalid.
1176            ChainTransactionError: If the transaction fails.
1177        """
1178
1179        assert isinstance(delegation_fee, int)
1180        params = {
1181            "netuid": netuid,
1182            "name": name,
1183            "address": address,
1184            "stake_delegation_fee": delegation_fee,
1185            "validator_weight_fee": weight_setting_delegation_fee,
1186            "metadata": metadata,
1187        }
1188
1189        response = self.compose_call("update_module", params=params, key=key)
1190
1191        return response
1192
1193    def register_module(
1194        self,
1195        key: Keypair,
1196        name: str,
1197        address: str | None = None,
1198        subnet: str = "Rootnet",
1199        metadata: str | None = None,
1200    ) -> ExtrinsicReceipt:
1201        """
1202        Registers a new module in the network.
1203
1204        Args:
1205            key: The keypair used for registering the module.
1206            name: The name of the module. If None, a default or previously
1207                set name is used. # How does this work?
1208            address: The address of the module. If None, a default or
1209                previously set address is used. # How does this work?
1210            subnet: The network subnet to register the module in.
1211            min_stake: The minimum stake required for the module, in nanotokens.
1212                If None, a default value is used.
1213
1214        Returns:
1215            A receipt of the registration transaction.
1216
1217        Raises:
1218            InvalidParameterError: If the provided parameters are invalid.
1219            ChainTransactionError: If the transaction fails.
1220        """
1221
1222        key_addr = key.ss58_address
1223
1224        params = {
1225            "network_name": subnet,
1226            "address": address,
1227            "name": name,
1228            "module_key": key_addr,
1229            "metadata": metadata,
1230        }
1231
1232        response = self.compose_call("register", params=params, key=key)
1233        return response
1234
1235    def deregister_module(self, key: Keypair, netuid: int) -> ExtrinsicReceipt:
1236        """
1237        Deregisters a module from the network.
1238
1239        Args:
1240            key: The keypair associated with the module's account.
1241            netuid: The network identifier.
1242
1243        Returns:
1244            A receipt of the module deregistration transaction.
1245
1246        Raises:
1247            ChainTransactionError: If the transaction fails.
1248        """
1249
1250        params = {"netuid": netuid}
1251
1252        response = self.compose_call("deregister", params=params, key=key)
1253
1254        return response
1255
1256    def register_subnet(
1257        self, key: Keypair, name: str, metadata: str | None = None
1258    ) -> ExtrinsicReceipt:
1259        """
1260        Registers a new subnet in the network.
1261
1262        Args:
1263            key (Keypair): The keypair used for registering the subnet.
1264            name (str): The name of the subnet to be registered.
1265            metadata (str | None, optional): Additional metadata for the subnet. Defaults to None.
1266
1267        Returns:
1268            ExtrinsicReceipt: A receipt of the subnet registration transaction.
1269
1270        Raises:
1271            ChainTransactionError: If the transaction fails.
1272        """
1273
1274        params = {
1275            "name": name,
1276            "metadata": metadata,
1277        }
1278
1279        response = self.compose_call("register_subnet", params=params, key=key)
1280
1281        return response
1282
1283    def vote(
1284        self,
1285        key: Keypair,
1286        uids: list[int],
1287        weights: list[int],
1288        netuid: int = 0,
1289    ) -> ExtrinsicReceipt:
1290        """
1291        Casts votes on a list of module UIDs with corresponding weights.
1292
1293        The length of the UIDs list and the weights list should be the same.
1294        Each weight corresponds to the UID at the same index.
1295
1296        Args:
1297            key: The keypair used for signing the vote transaction.
1298            uids: A list of module UIDs to vote on.
1299            weights: A list of weights corresponding to each UID.
1300            netuid: The network identifier.
1301
1302        Returns:
1303            A receipt of the voting transaction.
1304
1305        Raises:
1306            InvalidParameterError: If the lengths of UIDs and weights lists
1307                do not match.
1308            ChainTransactionError: If the transaction fails.
1309        """
1310
1311        assert len(uids) == len(weights)
1312
1313        params = {
1314            "uids": uids,
1315            "weights": weights,
1316            "netuid": netuid,
1317        }
1318
1319        response = self.compose_call(
1320            "set_weights",
1321            params=params,
1322            key=key,
1323            module="SubnetEmissionModule",
1324        )
1325
1326        return response
1327
1328    def delegate_weight_control(
1329        self, key: Keypair, target: Ss58Address, netuid: int
1330    ):
1331        """
1332        delegates weight setting control from the current account to the target account.
1333        Both accounts have to be registered and have a valid validator `spec`
1334        """
1335
1336        params = {
1337            "netuid": netuid,
1338            "target": target,
1339        }
1340
1341        response = self.compose_call(
1342            "delegate_weight_control",
1343            params=params,
1344            key=key,
1345            module="SubnetEmissionModule",
1346        )
1347        return response
1348
1349    def add_authorities(
1350        self,
1351        key: Keypair,
1352        new_authorities: list[tuple[Ss58Address, tuple[str, str]]],
1353    ):
1354        """
1355        only for the sudo key
1356        give the authorities as hexadecimal
1357        """
1358        params = {
1359            "new_authorities": new_authorities,
1360        }
1361        response = self.compose_call(
1362            "add_authorities",
1363            params=params,
1364            key=key,
1365            module="Offworker",
1366            sudo=True,
1367        )
1368        return response
1369
1370    def vote_encrypted(
1371        self,
1372        key: Keypair,
1373        uids: list[int],
1374        weights: list[int],
1375        netuid: int = 0,
1376    ) -> ExtrinsicReceipt:
1377        """
1378        Casts encrypted votes for module UIDs with corresponding weights.
1379
1380        Args:
1381            key (Keypair): The keypair used for signing the transaction.
1382            uids (list[int]): List of UIDs to vote for.
1383            weights (list[int]): List of weights corresponding to each UID.
1384            netuid (int, optional): Network UID. Defaults to 0.
1385
1386        Returns:
1387            ExtrinsicReceipt: Receipt of the submitted extrinsic.
1388
1389        Raises:
1390            ValueError: If there's a length mismatch between UIDs and weights,
1391                if subnet data is not found, or if subnet key format is invalid.
1392        """
1393        if len(uids) != len(weights):
1394            raise ValueError("Length mismatch between UIDs and weights")
1395
1396        subnet_data = self.query(
1397            "SubnetDecryptionData",
1398            module="SubnetEmissionModule",
1399            params=[netuid],
1400        )
1401        if not subnet_data:
1402            raise ValueError("Subnet data not found")
1403
1404        subnet_key = subnet_data.get("node_public_key")
1405        if not subnet_key or len(subnet_key) < 2:
1406            raise ValueError("Invalid subnet key format")
1407
1408        vote_data = list(zip(uids, weights))
1409        decryptor = (
1410            bytes_from_hex(subnet_key[0]),
1411            bytes_from_hex(subnet_key[1]),
1412        )
1413        validator_key = [int(x) for x in key.public_key]
1414
1415        encrypted_weights = encrypt_weights(decryptor, vote_data, validator_key)
1416        weights_hash = hashlib.sha256(str(weights).encode()).hexdigest()
1417
1418        params = {
1419            "uids": uids,
1420            "encrypted_weights": encrypted_weights,
1421            "netuid": netuid,
1422            "decrypted_weights_hash": weights_hash,
1423        }
1424
1425        return self.compose_call(
1426            "set_weights_encrypted",
1427            params=params,
1428            key=key,
1429            module="SubnetEmissionModule",
1430        )
1431
1432    def update_subnet(
1433        self,
1434        key: Keypair,
1435        params: SubnetParams,
1436        netuid: int = 0,
1437    ) -> ExtrinsicReceipt:
1438        """
1439        Update a subnet's configuration.
1440
1441        It requires the founder key for authorization.
1442
1443        Args:
1444            key: The founder keypair of the subnet.
1445            params: The new parameters for the subnet.
1446            netuid: The network identifier.
1447
1448        Returns:
1449            A receipt of the subnet update transaction.
1450
1451        Raises:
1452            AuthorizationError: If the key is not authorized.
1453            ChainTransactionError: If the transaction fails.
1454        """
1455
1456        general_params = dict(params)
1457        general_params["netuid"] = netuid
1458        if general_params.get("subnet_metadata") is None:
1459            general_params["metadata"] = None
1460        else:
1461            general_params["metadata"] = general_params["subnet_metadata"]
1462
1463        response = self.compose_call(
1464            fn="update_subnet",
1465            params=general_params,
1466            key=key,
1467        )
1468
1469        return response
1470
1471    def transfer_stake(
1472        self,
1473        key: Keypair,
1474        amount: int,
1475        from_module_key: Ss58Address,
1476        dest_module_address: Ss58Address,
1477    ) -> ExtrinsicReceipt:
1478        """
1479        Realocate staked tokens from one staked module to another module.
1480
1481        Args:
1482            key: The keypair associated with the account that is delegating the tokens.
1483            amount: The amount of staked tokens to transfer, in nanotokens.
1484            from_module_key: The SS58 address of the module you want to transfer from (currently delegated by the key).
1485            dest_module_address: The SS58 address of the destination (newly delegated key).
1486            netuid: The network identifier.
1487
1488        Returns:
1489            A receipt of the stake transfer transaction.
1490
1491        Raises:
1492            InsufficientStakeError: If the source module key does not have
1493            enough staked tokens. ChainTransactionError: If the transaction
1494            fails.
1495        """
1496
1497        amount = amount - self.get_existential_deposit()
1498
1499        params = {
1500            "amount": amount,
1501            "module_key": from_module_key,
1502            "new_module_key": dest_module_address,
1503        }
1504
1505        response = self.compose_call("transfer_stake", key=key, params=params)
1506
1507        return response
1508
1509    def bridge(
1510        self,
1511        key: Keypair,
1512        amount: int,
1513    ):
1514        """
1515        Bridge tokens from the Subspace network to the Torus network.
1516
1517        Args:
1518            key: The keypair associated with the account that is bridging the tokens.
1519            amount: The amount of tokens to bridge, in nanotokens.
1520
1521        Returns:
1522            A receipt of the bridging transaction.
1523
1524        Raises:
1525            InsufficientBalanceError: If the account does not have enough balance.
1526            ChainTransactionError: If the transaction fails.
1527        """
1528
1529        params = {"amount": amount}
1530
1531        response = self.compose_call("bridge", key=key, params=params)
1532
1533        return response
1534
1535    def bridge_withdraw(
1536        self,
1537        key: Keypair,
1538        amount: int,
1539    ):
1540        """
1541        Withdraw bridged tokens from the Torus network to the Subspace network.
1542
1543        Args:
1544            key: The keypair associated with the account that is withdrawing the tokens.
1545            amount: The amount of tokens to withdraw, in nanotokens.
1546
1547        Returns:
1548            A receipt of the withdrawal transaction.
1549
1550        Raises:
1551            InsufficientBalanceError: If the account does not have enough balance.
1552            ChainTransactionError: If the transaction fails.
1553        """
1554
1555        params = {"amount": amount}
1556
1557        response = self.compose_call("bridge_withdraw", key=key, params=params)
1558
1559        return response
1560
1561    def multiunstake(
1562        self,
1563        key: Keypair,
1564        keys: list[Ss58Address],
1565        amounts: list[int],
1566    ) -> ExtrinsicReceipt:
1567        """
1568        Unstakes tokens from multiple module keys.
1569
1570        And the lists `keys` and `amounts` must be of the same length. Each
1571        amount corresponds to the module key at the same index.
1572
1573        Args:
1574            key: The keypair associated with the unstaker's account.
1575            keys: A list of SS58 addresses of the module keys to unstake from.
1576            amounts: A list of amounts to unstake from each module key,
1577              in nanotokens.
1578            netuid: The network identifier.
1579
1580        Returns:
1581            A receipt of the multi-unstaking transaction.
1582
1583        Raises:
1584            MismatchedLengthError: If the lengths of keys and amounts lists do
1585            not match. InsufficientStakeError: If any of the module keys do not
1586            have enough staked tokens. ChainTransactionError: If the transaction
1587            fails.
1588        """
1589
1590        assert len(keys) == len(amounts)
1591
1592        params = {"module_keys": keys, "amounts": amounts}
1593
1594        response = self.compose_call(
1595            "remove_stake_multiple", params=params, key=key
1596        )
1597
1598        return response
1599
1600    def multistake(
1601        self,
1602        key: Keypair,
1603        keys: list[Ss58Address],
1604        amounts: list[int],
1605    ) -> ExtrinsicReceipt:
1606        """
1607        Stakes tokens to multiple module keys.
1608
1609        The lengths of the `keys` and `amounts` lists must be the same. Each
1610        amount corresponds to the module key at the same index.
1611
1612        Args:
1613            key: The keypair associated with the staker's account.
1614            keys: A list of SS58 addresses of the module keys to stake to.
1615            amounts: A list of amounts to stake to each module key,
1616                in nanotokens.
1617            netuid: The network identifier.
1618
1619        Returns:
1620            A receipt of the multi-staking transaction.
1621
1622        Raises:
1623            MismatchedLengthError: If the lengths of keys and amounts lists
1624                do not match.
1625            ChainTransactionError: If the transaction fails.
1626        """
1627
1628        assert len(keys) == len(amounts)
1629
1630        params = {
1631            "module_keys": keys,
1632            "amounts": amounts,
1633        }
1634
1635        response = self.compose_call(
1636            "add_stake_multiple", params=params, key=key
1637        )
1638
1639        return response
1640
1641    def add_profit_shares(
1642        self,
1643        key: Keypair,
1644        keys: list[Ss58Address],
1645        shares: list[int],
1646    ) -> ExtrinsicReceipt:
1647        """
1648        Allocates profit shares to multiple keys.
1649
1650        The lists `keys` and `shares` must be of the same length,
1651        with each share amount corresponding to the key at the same index.
1652
1653        Args:
1654            key: The keypair associated with the account
1655                distributing the shares.
1656            keys: A list of SS58 addresses to allocate shares to.
1657            shares: A list of share amounts to allocate to each key,
1658                in nanotokens.
1659
1660        Returns:
1661            A receipt of the profit sharing transaction.
1662
1663        Raises:
1664            MismatchedLengthError: If the lengths of keys and shares
1665                lists do not match.
1666            ChainTransactionError: If the transaction fails.
1667        """
1668
1669        assert len(keys) == len(shares)
1670
1671        params = {"keys": keys, "shares": shares}
1672
1673        response = self.compose_call(
1674            "add_profit_shares", params=params, key=key
1675        )
1676
1677        return response
1678
1679    def add_subnet_proposal(
1680        self, key: Keypair, params: dict[str, Any], ipfs: str, netuid: int = 0
1681    ) -> ExtrinsicReceipt:
1682        """
1683        Submits a proposal for creating or modifying a subnet within the
1684        network.
1685
1686        The proposal includes various parameters like the name, founder, share
1687        allocations, and other subnet-specific settings.
1688
1689        Args:
1690            key: The keypair used for signing the proposal transaction.
1691            params: The parameters for the subnet proposal.
1692            netuid: The network identifier.
1693
1694        Returns:
1695            A receipt of the subnet proposal transaction.
1696
1697        Raises:
1698            InvalidParameterError: If the provided subnet
1699                parameters are invalid.
1700            ChainTransactionError: If the transaction fails.
1701        """
1702
1703        general_params = dict(params)
1704        general_params["netuid"] = netuid
1705        general_params["data"] = ipfs
1706        if "metadata" not in general_params:
1707            general_params["metadata"] = None
1708
1709        # general_params["burn_config"] = json.dumps(general_params["burn_config"])
1710        response = self.compose_call(
1711            fn="add_subnet_params_proposal",
1712            params=general_params,
1713            key=key,
1714            module="GovernanceModule",
1715        )
1716
1717        return response
1718
1719    def add_custom_proposal(
1720        self,
1721        key: Keypair,
1722        cid: str,
1723    ) -> ExtrinsicReceipt:
1724        params = {"data": cid}
1725
1726        response = self.compose_call(
1727            fn="add_global_custom_proposal",
1728            params=params,
1729            key=key,
1730            module="GovernanceModule",
1731        )
1732        return response
1733
1734    def add_custom_subnet_proposal(
1735        self,
1736        key: Keypair,
1737        cid: str,
1738        netuid: int = 0,
1739    ) -> ExtrinsicReceipt:
1740        """
1741        Submits a proposal for creating or modifying a custom subnet within the
1742        network.
1743
1744        The proposal includes various parameters like the name, founder, share
1745        allocations, and other subnet-specific settings.
1746
1747        Args:
1748            key: The keypair used for signing the proposal transaction.
1749            params: The parameters for the subnet proposal.
1750            netuid: The network identifier.
1751
1752        Returns:
1753            A receipt of the subnet proposal transaction.
1754        """
1755
1756        params = {
1757            "data": cid,
1758            "netuid": netuid,
1759        }
1760
1761        response = self.compose_call(
1762            fn="add_subnet_custom_proposal",
1763            params=params,
1764            key=key,
1765            module="GovernanceModule",
1766        )
1767
1768        return response
1769
1770    def add_global_proposal(
1771        self,
1772        key: Keypair,
1773        params: NetworkParams,
1774        cid: str | None,
1775    ) -> ExtrinsicReceipt:
1776        """
1777        Submits a proposal for altering the global network parameters.
1778
1779        Allows for the submission of a proposal to
1780        change various global parameters
1781        of the network, such as emission rates, rate limits, and voting
1782        thresholds. It is used to
1783        suggest changes that affect the entire network's operation.
1784
1785        Args:
1786            key: The keypair used for signing the proposal transaction.
1787            params: A dictionary containing global network parameters
1788                    like maximum allowed subnets, modules,
1789                    transaction rate limits, and others.
1790
1791        Returns:
1792            A receipt of the global proposal transaction.
1793
1794        Raises:
1795            InvalidParameterError: If the provided network
1796                parameters are invalid.
1797            ChainTransactionError: If the transaction fails.
1798        """
1799        general_params = cast(dict[str, Any], params)
1800        cid = cid or ""
1801        general_params["data"] = cid
1802
1803        response = self.compose_call(
1804            fn="add_global_params_proposal",
1805            params=general_params,
1806            key=key,
1807            module="GovernanceModule",
1808        )
1809
1810        return response
1811
1812    def vote_on_proposal(
1813        self,
1814        key: Keypair,
1815        proposal_id: int,
1816        agree: bool,
1817    ) -> ExtrinsicReceipt:
1818        """
1819        Casts a vote on a specified proposal within the network.
1820
1821        Args:
1822            key: The keypair used for signing the vote transaction.
1823            proposal_id: The unique identifier of the proposal to vote on.
1824
1825        Returns:
1826            A receipt of the voting transaction in nanotokens.
1827
1828        Raises:
1829            InvalidProposalIDError: If the provided proposal ID does not
1830                exist or is invalid.
1831            ChainTransactionError: If the transaction fails.
1832        """
1833
1834        params = {"proposal_id": proposal_id, "agree": agree}
1835
1836        response = self.compose_call(
1837            "vote_proposal",
1838            key=key,
1839            params=params,
1840            module="GovernanceModule",
1841        )
1842
1843        return response
1844
1845    def unvote_on_proposal(
1846        self,
1847        key: Keypair,
1848        proposal_id: int,
1849    ) -> ExtrinsicReceipt:
1850        """
1851        Retracts a previously cast vote on a specified proposal.
1852
1853        Args:
1854            key: The keypair used for signing the unvote transaction.
1855            proposal_id: The unique identifier of the proposal to withdraw the
1856                vote from.
1857
1858        Returns:
1859            A receipt of the unvoting transaction in nanotokens.
1860
1861        Raises:
1862            InvalidProposalIDError: If the provided proposal ID does not
1863                exist or is invalid.
1864            ChainTransactionError: If the transaction fails to be processed, or
1865                if there was no prior vote to retract.
1866        """
1867
1868        params = {"proposal_id": proposal_id}
1869
1870        response = self.compose_call(
1871            "remove_vote_proposal",
1872            key=key,
1873            params=params,
1874            module="GovernanceModule",
1875        )
1876
1877        return response
1878
1879    def enable_vote_power_delegation(self, key: Keypair) -> ExtrinsicReceipt:
1880        """
1881        Enables vote power delegation for the signer's account.
1882
1883        Args:
1884            key: The keypair used for signing the delegation transaction.
1885
1886        Returns:
1887            A receipt of the vote power delegation transaction.
1888
1889        Raises:
1890            ChainTransactionError: If the transaction fails.
1891        """
1892
1893        response = self.compose_call(
1894            "enable_vote_power_delegation",
1895            params={},
1896            key=key,
1897            module="GovernanceModule",
1898        )
1899
1900        return response
1901
1902    def disable_vote_power_delegation(self, key: Keypair) -> ExtrinsicReceipt:
1903        """
1904        Disables vote power delegation for the signer's account.
1905
1906        Args:
1907            key: The keypair used for signing the delegation transaction.
1908
1909        Returns:
1910            A receipt of the vote power delegation transaction.
1911
1912        Raises:
1913            ChainTransactionError: If the transaction fails.
1914        """
1915
1916        response = self.compose_call(
1917            "disable_vote_power_delegation",
1918            params={},
1919            key=key,
1920            module="GovernanceModule",
1921        )
1922
1923        return response
1924
1925    def add_dao_application(
1926        self, key: Keypair, application_key: Ss58Address, data: str
1927    ) -> ExtrinsicReceipt:
1928        """
1929        Submits a new application to the general subnet DAO.
1930
1931        Args:
1932            key: The keypair used for signing the application transaction.
1933            application_key: The SS58 address of the application key.
1934            data: The data associated with the application.
1935
1936        Returns:
1937            A receipt of the application transaction.
1938
1939        Raises:
1940            ChainTransactionError: If the transaction fails.
1941        """
1942
1943        params = {"application_key": application_key, "data": data}
1944
1945        response = self.compose_call(
1946            "add_dao_application",
1947            module="GovernanceModule",
1948            key=key,
1949            params=params,
1950        )
1951
1952        return response
1953
1954    def query_map_curator_applications(self) -> dict[str, dict[str, str]]:
1955        query_result = self.query_map(
1956            "CuratorApplications",
1957            module="GovernanceModule",
1958            params=[],
1959            extract_value=False,
1960        )
1961        applications = query_result.get("CuratorApplications", {})
1962        return applications
1963
1964    def query_map_proposals(
1965        self, extract_value: bool = False
1966    ) -> dict[int, dict[str, Any]]:
1967        """
1968        Retrieves a mappping of proposals from the network.
1969
1970        Queries the network and returns a mapping of proposal IDs to
1971        their respective parameters.
1972
1973        Returns:
1974            A dictionary mapping proposal IDs
1975            to dictionaries of their parameters.
1976
1977        Raises:
1978            QueryError: If the query to the network fails or is invalid.
1979        """
1980
1981        return self.query_map(
1982            "Proposals", extract_value=extract_value, module="GovernanceModule"
1983        )["Proposals"]
1984
1985    def query_map_weights(
1986        self, netuid: int = 0, extract_value: bool = False
1987    ) -> dict[int, list[tuple[int, int]]] | None:
1988        """
1989        Retrieves a mapping of weights for keys on the network.
1990
1991        Queries the network and returns a mapping of key UIDs to
1992        their respective weights.
1993
1994        Args:
1995            netuid: The network UID from which to get the weights.
1996
1997        Returns:
1998            A dictionary mapping key UIDs to lists of their weights.
1999
2000        Raises:
2001            QueryError: If the query to the network fails or is invalid.
2002        """
2003
2004        weights_dict = self.query_map(
2005            "Weights",
2006            [netuid],
2007            extract_value=extract_value,
2008            module="SubnetEmissionModule",
2009        ).get("Weights")
2010        return weights_dict
2011
2012    def query_map_key(
2013        self,
2014        netuid: int = 0,
2015        extract_value: bool = False,
2016    ) -> dict[int, Ss58Address]:
2017        """
2018        Retrieves a map of keys from the network.
2019
2020        Fetches a mapping of key UIDs to their associated
2021        addresses on the network.
2022        The query can be targeted at a specific network UID if required.
2023
2024        Args:
2025            netuid: The network UID from which to get the keys.
2026
2027        Returns:
2028            A dictionary mapping key UIDs to their addresses.
2029
2030        Raises:
2031            QueryError: If the query to the network fails or is invalid.
2032        """
2033        return self.query_map("Keys", [netuid], extract_value=extract_value)[
2034            "Keys"
2035        ]
2036
2037    def query_map_address(
2038        self, netuid: int = 0, extract_value: bool = False
2039    ) -> dict[int, str]:
2040        """
2041        Retrieves a map of key addresses from the network.
2042
2043        Queries the network for a mapping of key UIDs to their addresses.
2044
2045        Args:
2046            netuid: The network UID from which to get the addresses.
2047
2048        Returns:
2049            A dictionary mapping key UIDs to their addresses.
2050
2051        Raises:
2052            QueryError: If the query to the network fails or is invalid.
2053        """
2054
2055        return self.query_map("Address", [netuid], extract_value=extract_value)[
2056            "Address"
2057        ]
2058
2059    def query_map_emission(
2060        self, extract_value: bool = False
2061    ) -> dict[int, list[int]]:
2062        """
2063        Retrieves a map of emissions for keys on the network.
2064
2065        Queries the network to get a mapping of
2066        key UIDs to their emission values.
2067
2068        Returns:
2069            A dictionary mapping key UIDs to lists of their emission values.
2070
2071        Raises:
2072            QueryError: If the query to the network fails or is invalid.
2073        """
2074
2075        return self.query_map("Emission", extract_value=extract_value)[
2076            "Emission"
2077        ]
2078
2079    def query_map_pending_emission(self, extract_value: bool = False) -> int:
2080        """
2081        Retrieves a map of pending emissions for the subnets.
2082
2083        Queries the network for a mapping of subnet UIDs to their pending emission values.
2084
2085        Returns:
2086            A dictionary mapping subnet UIDs to their pending emission values.
2087
2088        Raises:
2089            QueryError: If the query to the network fails or is invalid.
2090        """
2091        return self.query_map(
2092            "PendingEmission",
2093            extract_value=extract_value,
2094            module="SubnetEmissionModule",
2095        )["PendingEmission"]
2096
2097    def query_map_subnet_emission(
2098        self, extract_value: bool = False
2099    ) -> dict[int, int]:
2100        """
2101        Retrieves a map of subnet emissions for the network.
2102
2103        Queries the network for a mapping of subnet UIDs to their emission values.
2104
2105        Returns:
2106            A dictionary mapping subnet UIDs to their emission values.
2107
2108        Raises:
2109            QueryError: If the query to the network fails or is invalid.
2110        """
2111
2112        return self.query_map(
2113            "SubnetEmission",
2114            extract_value=extract_value,
2115            module="SubnetEmissionModule",
2116        )["SubnetEmission"]
2117
2118    def query_map_subnet_consensus(
2119        self, extract_value: bool = False
2120    ) -> dict[int, str]:
2121        """
2122        Retrieves a map of subnet consensus types for the network.
2123
2124        Queries the network for a mapping of subnet UIDs to their consensus types.
2125
2126        Returns:
2127            A dictionary mapping subnet UIDs to their consensus types.
2128
2129        Raises:
2130            QueryError: If the query to the network fails or is invalid.
2131        """
2132
2133        return self.query_map(
2134            "SubnetConsensusType",
2135            extract_value=extract_value,
2136            module="SubnetEmissionModule",
2137        )["SubnetConsensusType"]
2138
2139    def query_map_incentive(
2140        self, extract_value: bool = False
2141    ) -> dict[int, list[int]]:
2142        """
2143        Retrieves a mapping of incentives for keys on the network.
2144
2145        Queries the network and returns a mapping of key UIDs to
2146        their respective incentive values.
2147
2148        Returns:
2149            A dictionary mapping key UIDs to lists of their incentive values.
2150
2151        Raises:
2152            QueryError: If the query to the network fails or is invalid.
2153        """
2154
2155        return self.query_map("Incentive", extract_value=extract_value)[
2156            "Incentive"
2157        ]
2158
2159    def query_map_dividend(
2160        self, extract_value: bool = False
2161    ) -> dict[int, list[int]]:
2162        """
2163        Retrieves a mapping of dividends for keys on the network.
2164
2165        Queries the network for a mapping of key UIDs to
2166        their dividend values.
2167
2168        Returns:
2169            A dictionary mapping key UIDs to lists of their dividend values.
2170
2171        Raises:
2172            QueryError: If the query to the network fails or is invalid.
2173        """
2174
2175        return self.query_map("Dividends", extract_value=extract_value)[
2176            "Dividends"
2177        ]
2178
2179    def query_map_regblock(
2180        self, netuid: int = 0, extract_value: bool = False
2181    ) -> dict[int, int]:
2182        """
2183        Retrieves a mapping of registration blocks for keys on the network.
2184
2185        Queries the network for a mapping of key UIDs to
2186        the blocks where they were registered.
2187
2188        Args:
2189            netuid: The network UID from which to get the registration blocks.
2190
2191        Returns:
2192            A dictionary mapping key UIDs to their registration blocks.
2193
2194        Raises:
2195            QueryError: If the query to the network fails or is invalid.
2196        """
2197
2198        return self.query_map(
2199            "RegistrationBlock", [netuid], extract_value=extract_value
2200        )["RegistrationBlock"]
2201
2202    def query_map_lastupdate(
2203        self, extract_value: bool = False
2204    ) -> dict[int, list[int]]:
2205        """
2206        Retrieves a mapping of the last update times for keys on the network.
2207
2208        Queries the network for a mapping of key UIDs to their last update times.
2209
2210        Returns:
2211            A dictionary mapping key UIDs to lists of their last update times.
2212
2213        Raises:
2214            QueryError: If the query to the network fails or is invalid.
2215        """
2216
2217        return self.query_map("LastUpdate", extract_value=extract_value)[
2218            "LastUpdate"
2219        ]
2220
2221    def query_map_stakefrom(
2222        self, extract_value: bool = False
2223    ) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
2224        """
2225        Retrieves a mapping of stakes from various sources for keys on the network.
2226
2227        Queries the network to obtain a mapping of key addresses to the sources
2228        and amounts of stakes they have received.
2229
2230        Args:
2231            netuid: The network UID from which to get the stakes.
2232
2233        Returns:
2234            A dictionary mapping key addresses to lists of tuples
2235            (module_key_address, amount).
2236
2237        Raises:
2238            QueryError: If the query to the network fails or is invalid.
2239        """
2240
2241        result = self.query_map("StakeFrom", [], extract_value=extract_value)[
2242            "StakeFrom"
2243        ]
2244
2245        return transform_stake_dmap(result)
2246
2247    def query_map_staketo(
2248        self, extract_value: bool = False
2249    ) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
2250        """
2251        Retrieves a mapping of stakes to destinations for keys on the network.
2252
2253        Queries the network for a mapping of key addresses to the destinations
2254        and amounts of stakes they have made.
2255
2256        Args:
2257            netuid: The network UID from which to get the stakes.
2258
2259        Returns:
2260            A dictionary mapping key addresses to lists of tuples
2261            (module_key_address, amount).
2262
2263        Raises:
2264            QueryError: If the query to the network fails or is invalid.
2265        """
2266
2267        result = self.query_map("StakeTo", [], extract_value=extract_value)[
2268            "StakeTo"
2269        ]
2270        return transform_stake_dmap(result)
2271
2272    def query_map_delegationfee(
2273        self, netuid: int = 0, extract_value: bool = False
2274    ) -> dict[str, int]:
2275        """
2276        Retrieves a mapping of delegation fees for keys on the network.
2277
2278        Queries the network to obtain a mapping of key addresses to their
2279        respective delegation fees.
2280
2281        Args:
2282            netuid: The network UID to filter the delegation fees.
2283
2284        Returns:
2285            A dictionary mapping key addresses to their delegation fees.
2286
2287        Raises:
2288            QueryError: If the query to the network fails or is invalid.
2289        """
2290
2291        return self.query_map(
2292            "DelegationFee", [netuid], extract_value=extract_value
2293        )["DelegationFee"]
2294
2295    def query_map_tempo(self, extract_value: bool = False) -> dict[int, int]:
2296        """
2297        Retrieves a mapping of tempo settings for the network.
2298
2299        Queries the network to obtain the tempo (rate of reward distributions)
2300        settings for various network subnets.
2301
2302        Returns:
2303            A dictionary mapping network UIDs to their tempo settings.
2304
2305        Raises:
2306            QueryError: If the query to the network fails or is invalid.
2307        """
2308
2309        return self.query_map("Tempo", extract_value=extract_value)["Tempo"]
2310
2311    def query_map_immunity_period(self, extract_value: bool) -> dict[int, int]:
2312        """
2313        Retrieves a mapping of immunity periods for the network.
2314
2315        Queries the network for the immunity period settings,
2316        which represent the time duration during which modules
2317        can not get deregistered.
2318
2319        Returns:
2320            A dictionary mapping network UIDs to their immunity period settings.
2321
2322        Raises:
2323            QueryError: If the query to the network fails or is invalid.
2324        """
2325
2326        return self.query_map("ImmunityPeriod", extract_value=extract_value)[
2327            "ImmunityPeriod"
2328        ]
2329
2330    def query_map_min_allowed_weights(
2331        self, extract_value: bool = False
2332    ) -> dict[int, int]:
2333        """
2334        Retrieves a mapping of minimum allowed weights for the network.
2335
2336        Queries the network to obtain the minimum allowed weights,
2337        which are the lowest permissible weight values that can be set by
2338        validators.
2339
2340        Returns:
2341            A dictionary mapping network UIDs to
2342            their minimum allowed weight values.
2343
2344        Raises:
2345            QueryError: If the query to the network fails or is invalid.
2346        """
2347
2348        return self.query_map("MinAllowedWeights", extract_value=extract_value)[
2349            "MinAllowedWeights"
2350        ]
2351
2352    def query_map_max_allowed_weights(
2353        self, extract_value: bool = False
2354    ) -> dict[int, int]:
2355        """
2356        Retrieves a mapping of maximum allowed weights for the network.
2357
2358        Queries the network for the maximum allowed weights,
2359        which are the highest permissible
2360        weight values that can be set by validators.
2361
2362        Returns:
2363            A dictionary mapping network UIDs to
2364            their maximum allowed weight values.
2365
2366        Raises:
2367            QueryError: If the query to the network fails or is invalid.
2368        """
2369
2370        return self.query_map("MaxAllowedWeights", extract_value=extract_value)[
2371            "MaxAllowedWeights"
2372        ]
2373
2374    def query_map_max_allowed_uids(
2375        self, extract_value: bool = False
2376    ) -> dict[int, int]:
2377        """
2378        Queries the network for the maximum number of allowed user IDs (UIDs)
2379        for each network subnet.
2380
2381        Fetches a mapping of network subnets to their respective
2382        limits on the number of user IDs that can be created or used.
2383
2384        Returns:
2385            A dictionary mapping network UIDs (unique identifiers) to their
2386            maximum allowed number of UIDs.
2387            Each entry represents a network subnet
2388            with its corresponding UID limit.
2389
2390        Raises:
2391            QueryError: If the query to the network fails or is invalid.
2392        """
2393
2394        return self.query_map("MaxAllowedUids", extract_value=extract_value)[
2395            "MaxAllowedUids"
2396        ]
2397
2398    def query_map_min_stake(
2399        self, extract_value: bool = False
2400    ) -> dict[int, int]:
2401        """
2402        Retrieves a mapping of minimum allowed stake on the network.
2403
2404        Queries the network to obtain the minimum number of stake,
2405        which is represented in nanotokens.
2406
2407        Returns:
2408            A dictionary mapping network UIDs to
2409            their minimum allowed stake values.
2410
2411        Raises:
2412            QueryError: If the query to the network fails or is invalid.
2413        """
2414
2415        return self.query_map("MinStake", extract_value=extract_value)[
2416            "MinStake"
2417        ]
2418
2419    def query_map_max_stake(
2420        self, extract_value: bool = False
2421    ) -> dict[int, int]:
2422        """
2423        Retrieves a mapping of the maximum stake values for the network.
2424
2425        Queries the network for the maximum stake values across various s
2426        ubnets of the network.
2427
2428        Returns:
2429            A dictionary mapping network UIDs to their maximum stake values.
2430
2431        Raises:
2432            QueryError: If the query to the network fails or is invalid.
2433        """
2434
2435        return self.query_map("MaxStake", extract_value=extract_value)[
2436            "MaxStake"
2437        ]
2438
2439    def query_map_founder(self, extract_value: bool = False) -> dict[int, str]:
2440        """
2441        Retrieves a mapping of founders for the network.
2442
2443        Queries the network to obtain the founders associated with
2444        various subnets.
2445
2446        Returns:
2447            A dictionary mapping network UIDs to their respective founders.
2448
2449        Raises:
2450            QueryError: If the query to the network fails or is invalid.
2451        """
2452
2453        return self.query_map("Founder", extract_value=extract_value)["Founder"]
2454
2455    def query_map_founder_share(
2456        self, extract_value: bool = False
2457    ) -> dict[int, int]:
2458        """
2459        Retrieves a mapping of founder shares for the network.
2460
2461        Queries the network for the share percentages
2462        allocated to founders across different subnets.
2463
2464        Returns:
2465            A dictionary mapping network UIDs to their founder share percentages.
2466
2467        Raises:
2468            QueryError: If the query to the network fails or is invalid.
2469        """
2470
2471        return self.query_map("FounderShare", extract_value=extract_value)[
2472            "FounderShare"
2473        ]
2474
2475    def query_map_incentive_ratio(
2476        self, extract_value: bool = False
2477    ) -> dict[int, int]:
2478        """
2479        Retrieves a mapping of incentive ratios for the network.
2480
2481        Queries the network for the incentive ratios,
2482        which are the proportions of rewards or incentives
2483        allocated in different subnets of the network.
2484
2485        Returns:
2486            A dictionary mapping network UIDs to their incentive ratios.
2487
2488        Raises:
2489            QueryError: If the query to the network fails or is invalid.
2490        """
2491
2492        return self.query_map("IncentiveRatio", extract_value=extract_value)[
2493            "IncentiveRatio"
2494        ]
2495
2496    def query_map_trust_ratio(
2497        self, extract_value: bool = False
2498    ) -> dict[int, int]:
2499        """
2500        Retrieves a mapping of trust ratios for the network.
2501
2502        Queries the network for trust ratios,
2503        indicative of the level of trust or credibility assigned
2504        to different subnets of the network.
2505
2506        Returns:
2507            A dictionary mapping network UIDs to their trust ratios.
2508
2509        Raises:
2510            QueryError: If the query to the network fails or is invalid.
2511        """
2512
2513        return self.query_map("TrustRatio", extract_value=extract_value)[
2514            "TrustRatio"
2515        ]
2516
2517    def query_map_vote_mode_subnet(
2518        self, extract_value: bool = False
2519    ) -> dict[int, str]:
2520        """
2521        Retrieves a mapping of vote modes for subnets within the network.
2522
2523        Queries the network for the voting modes used in different
2524        subnets, which define the methodology or approach of voting within those
2525        subnets.
2526
2527        Returns:
2528            A dictionary mapping network UIDs to their vote
2529            modes for subnets.
2530
2531        Raises:
2532            QueryError: If the query to the network fails or is invalid.
2533        """
2534
2535        return self.query_map("VoteModeSubnet", extract_value=extract_value)[
2536            "VoteModeSubnet"
2537        ]
2538
2539    def query_map_legit_whitelist(
2540        self, extract_value: bool = False
2541    ) -> dict[Ss58Address, int]:
2542        """
2543        Retrieves a mapping of whitelisted addresses for the network.
2544
2545        Queries the network for a mapping of whitelisted addresses
2546        and their respective legitimacy status.
2547
2548        Returns:
2549            A dictionary mapping addresses to their legitimacy status.
2550
2551        Raises:
2552            QueryError: If the query to the network fails or is invalid.
2553        """
2554
2555        return self.query_map(
2556            "LegitWhitelist",
2557            module="GovernanceModule",
2558            extract_value=extract_value,
2559        )["LegitWhitelist"]
2560
2561    def query_map_subnet_names(
2562        self, extract_value: bool = False
2563    ) -> dict[int, str]:
2564        """
2565        Retrieves a mapping of subnet names within the network.
2566
2567        Queries the network for the names of various subnets,
2568        providing an overview of the different
2569        subnets within the network.
2570
2571        Returns:
2572            A dictionary mapping network UIDs to their subnet names.
2573
2574        Raises:
2575            QueryError: If the query to the network fails or is invalid.
2576        """
2577
2578        return self.query_map("SubnetNames", extract_value=extract_value)[
2579            "SubnetNames"
2580        ]
2581
2582    def query_map_balances(
2583        self, extract_value: bool = False, block_hash: str | None = None
2584    ) -> dict[str, dict[str, int | dict[str, int | float]]]:
2585        """
2586        Retrieves a mapping of account balances within the network.
2587
2588        Queries the network for the balances associated with different accounts.
2589        It provides detailed information including various types of
2590        balances for each account.
2591
2592        Returns:
2593            A dictionary mapping account addresses to their balance details.
2594
2595        Raises:
2596            QueryError: If the query to the network fails or is invalid.
2597        """
2598
2599        return self.query_map(
2600            "Account",
2601            module="System",
2602            extract_value=extract_value,
2603            block_hash=block_hash,
2604        )["Account"]
2605
2606    def query_map_registration_blocks(
2607        self, netuid: int = 0, extract_value: bool = False
2608    ) -> dict[int, int]:
2609        """
2610        Retrieves a mapping of registration blocks for UIDs on the network.
2611
2612        Queries the network to find the block numbers at which various
2613        UIDs were registered.
2614
2615        Args:
2616            netuid: The network UID from which to get the registrations.
2617
2618        Returns:
2619            A dictionary mapping UIDs to their registration block numbers.
2620
2621        Raises:
2622            QueryError: If the query to the network fails or is invalid.
2623        """
2624
2625        return self.query_map(
2626            "RegistrationBlock", [netuid], extract_value=extract_value
2627        )["RegistrationBlock"]
2628
2629    def query_map_name(
2630        self, netuid: int = 0, extract_value: bool = False
2631    ) -> dict[int, str]:
2632        """
2633        Retrieves a mapping of names for keys on the network.
2634
2635        Queries the network for the names associated with different keys.
2636        It provides a mapping of key UIDs to their registered names.
2637
2638        Args:
2639            netuid: The network UID from which to get the names.
2640
2641        Returns:
2642            A dictionary mapping key UIDs to their names.
2643
2644        Raises:
2645            QueryError: If the query to the network fails or is invalid.
2646        """
2647
2648        return self.query_map("Name", [netuid], extract_value=extract_value)[
2649            "Name"
2650        ]
2651
2652    #  == QUERY FUNCTIONS == #
2653
2654    def get_immunity_period(self, netuid: int = 0) -> int:
2655        """
2656        Queries the network for the immunity period setting.
2657
2658        The immunity period is a time duration during which a module
2659        can not be deregistered from the network.
2660        Fetches the immunity period for a specified network subnet.
2661
2662        Args:
2663            netuid: The network UID for which to query the immunity period.
2664
2665        Returns:
2666            The immunity period setting for the specified network subnet.
2667
2668        Raises:
2669            QueryError: If the query to the network fails or is invalid.
2670        """
2671
2672        return self.query(
2673            "ImmunityPeriod",
2674            params=[netuid],
2675        )
2676
2677    def get_max_set_weights_per_epoch(self):
2678        return self.query("MaximumSetWeightCallsPerEpoch")
2679
2680    def get_min_allowed_weights(self, netuid: int = 0) -> int:
2681        """
2682        Queries the network for the minimum allowed weights setting.
2683
2684        Retrieves the minimum weight values that are possible to set
2685        by a validator within a specific network subnet.
2686
2687        Args:
2688            netuid: The network UID for which to query the minimum allowed
2689              weights.
2690
2691        Returns:
2692            The minimum allowed weight values for the specified network
2693              subnet.
2694
2695        Raises:
2696            QueryError: If the query to the network fails or is invalid.
2697        """
2698
2699        return self.query(
2700            "MinAllowedWeights",
2701            params=[netuid],
2702        )
2703
2704    def get_dao_treasury_address(self) -> Ss58Address:
2705        return self.query("DaoTreasuryAddress", module="GovernanceModule")
2706
2707    def get_max_allowed_weights(self, netuid: int = 0) -> int:
2708        """
2709        Queries the network for the maximum allowed weights setting.
2710
2711        Retrieves the maximum weight values that are possible to set
2712        by a validator within a specific network subnet.
2713
2714        Args:
2715            netuid: The network UID for which to query the maximum allowed
2716              weights.
2717
2718        Returns:
2719            The maximum allowed weight values for the specified network
2720              subnet.
2721
2722        Raises:
2723            QueryError: If the query to the network fails or is invalid.
2724        """
2725
2726        return self.query("MaxAllowedWeights", params=[netuid])
2727
2728    def get_max_allowed_uids(self, netuid: int = 0) -> int:
2729        """
2730        Queries the network for the maximum allowed UIDs setting.
2731
2732        Fetches the upper limit on the number of user IDs that can
2733        be allocated or used within a specific network subnet.
2734
2735        Args:
2736            netuid: The network UID for which to query the maximum allowed UIDs.
2737
2738        Returns:
2739            The maximum number of allowed UIDs for the specified network subnet.
2740
2741        Raises:
2742            QueryError: If the query to the network fails or is invalid.
2743        """
2744
2745        return self.query("MaxAllowedUids", params=[netuid])
2746
2747    def get_name(self, netuid: int = 0) -> str:
2748        """
2749        Queries the network for the name of a specific subnet.
2750
2751        Args:
2752            netuid: The network UID for which to query the name.
2753
2754        Returns:
2755            The name of the specified network subnet.
2756
2757        Raises:
2758            QueryError: If the query to the network fails or is invalid.
2759        """
2760
2761        return self.query("Name", params=[netuid])
2762
2763    def get_subnet_name(self, netuid: int = 0) -> str:
2764        """
2765        Queries the network for the name of a specific subnet.
2766
2767        Args:
2768            netuid: The network UID for which to query the name.
2769
2770        Returns:
2771            The name of the specified network subnet.
2772
2773        Raises:
2774            QueryError: If the query to the network fails or is invalid.
2775        """
2776
2777        return self.query("SubnetNames", params=[netuid])
2778
2779    def get_global_dao_treasury(self):
2780        return self.query("GlobalDaoTreasury", module="GovernanceModule")
2781
2782    def get_n(self, netuid: int = 0) -> int:
2783        """
2784        Queries the network for the 'N' hyperparameter, which represents how
2785        many modules are on the network.
2786
2787        Args:
2788            netuid: The network UID for which to query the 'N' hyperparameter.
2789
2790        Returns:
2791            The value of the 'N' hyperparameter for the specified network
2792              subnet.
2793
2794        Raises:
2795            QueryError: If the query to the network fails or is invalid.
2796        """
2797
2798        return self.query("N", params=[netuid])
2799
2800    def get_tempo(self, netuid: int = 0) -> int:
2801        """
2802        Queries the network for the tempo setting, measured in blocks, for the
2803        specified subnet.
2804
2805        Args:
2806            netuid: The network UID for which to query the tempo.
2807
2808        Returns:
2809            The tempo setting for the specified subnet.
2810
2811        Raises:
2812            QueryError: If the query to the network fails or is invalid.
2813        """
2814
2815        return self.query("Tempo", params=[netuid])
2816
2817    def get_total_free_issuance(self, block_hash: str | None = None) -> int:
2818        """
2819        Queries the network for the total free issuance.
2820
2821        Fetches the total amount of free issuance tokens available
2822
2823        Returns:
2824            The total free issuance amount.
2825
2826        Raises:
2827            QueryError: If the query to the network fails or is invalid.
2828        """
2829
2830        return self.query(
2831            "TotalIssuance", module="Balances", block_hash=block_hash
2832        )
2833
2834    def get_total_stake(self, block_hash: str | None = None) -> int:
2835        """
2836        Retrieves a mapping of total stakes for keys on the network.
2837
2838        Queries the network for a mapping of key UIDs to their total stake amounts.
2839
2840        Returns:
2841            A dictionary mapping key UIDs to their total stake amounts.
2842
2843        Raises:
2844            QueryError: If the query to the network fails or is invalid.
2845        """
2846
2847        return self.query("TotalStake", block_hash=block_hash)
2848
2849    def get_registrations_per_block(self):
2850        """
2851        Queries the network for the number of registrations per block.
2852
2853        Fetches the number of registrations that are processed per
2854        block within the network.
2855
2856        Returns:
2857            The number of registrations processed per block.
2858
2859        Raises:
2860            QueryError: If the query to the network fails or is invalid.
2861        """
2862
2863        return self.query(
2864            "RegistrationsPerBlock",
2865        )
2866
2867    def max_registrations_per_block(self, netuid: int = 0):
2868        """
2869        Queries the network for the maximum number of registrations per block.
2870
2871        Retrieves the upper limit of registrations that can be processed in
2872        each block within a specific network subnet.
2873
2874        Args:
2875            netuid: The network UID for which to query.
2876
2877        Returns:
2878            The maximum number of registrations per block for
2879            the specified network subnet.
2880
2881        Raises:
2882            QueryError: If the query to the network fails or is invalid.
2883        """
2884
2885        return self.query(
2886            "MaxRegistrationsPerBlock",
2887            params=[netuid],
2888        )
2889
2890    def get_proposal(self, proposal_id: int = 0):
2891        """
2892        Queries the network for a specific proposal.
2893
2894        Args:
2895            proposal_id: The ID of the proposal to query.
2896
2897        Returns:
2898            The details of the specified proposal.
2899
2900        Raises:
2901            QueryError: If the query to the network fails, is invalid,
2902                or if the proposal ID does not exist.
2903        """
2904
2905        return self.query(
2906            "Proposals",
2907            params=[proposal_id],
2908        )
2909
2910    def get_trust(self, netuid: int = 0):
2911        """
2912        Queries the network for the trust setting of a specific network subnet.
2913
2914        Retrieves the trust level or score, which may represent the
2915        level of trustworthiness or reliability within a
2916        particular network subnet.
2917
2918        Args:
2919            netuid: The network UID for which to query the trust setting.
2920
2921        Returns:
2922            The trust level or score for the specified network subnet.
2923
2924        Raises:
2925            QueryError: If the query to the network fails or is invalid.
2926        """
2927
2928        return self.query(
2929            "Trust",
2930            params=[netuid],
2931        )
2932
2933    def get_uids(self, key: Ss58Address, netuid: int = 0) -> bool | None:
2934        """
2935        Queries the network for module UIDs associated with a specific key.
2936
2937        Args:
2938            key: The key address for which to query UIDs.
2939            netuid: The network UID within which to search for the key.
2940
2941        Returns:
2942            A list of UIDs associated with the specified key.
2943
2944        Raises:
2945            QueryError: If the query to the network fails or is invalid.
2946        """
2947
2948        return self.query(
2949            "Uids",
2950            params=[netuid, key],
2951        )
2952
2953    def get_unit_emission(self) -> int:
2954        """
2955        Queries the network for the unit emission setting.
2956
2957        Retrieves the unit emission value, which represents the
2958        emission rate or quantity for the $COMM token.
2959
2960        Returns:
2961            The unit emission value in nanos for the network.
2962
2963        Raises:
2964            QueryError: If the query to the network fails or is invalid.
2965        """
2966
2967        return self.query("UnitEmission", module="SubnetEmissionModule")
2968
2969    def get_tx_rate_limit(self) -> int:
2970        """
2971        Queries the network for the transaction rate limit.
2972
2973        Retrieves the rate limit for transactions within the network,
2974        which defines the maximum number of transactions that can be
2975        processed within a certain timeframe.
2976
2977        Returns:
2978            The transaction rate limit for the network.
2979
2980        Raises:
2981            QueryError: If the query to the network fails or is invalid.
2982        """
2983
2984        return self.query(
2985            "TxRateLimit",
2986        )
2987
2988    def get_subnet_burn(self) -> int:
2989        """Queries the network for the subnet burn value.
2990
2991        Retrieves the subnet burn value from the network, which represents
2992        the amount of tokens that are burned (permanently removed from
2993        circulation) for subnet-related operations.
2994
2995        Returns:
2996            int: The subnet burn value.
2997
2998        Raises:
2999            QueryError: If the query to the network fails or returns invalid data.
3000        """
3001
3002        return self.query(
3003            "SubnetBurn",
3004        )
3005
3006    def get_burn_rate(self) -> int:
3007        """
3008        Queries the network for the burn rate setting.
3009
3010        Retrieves the burn rate, which represents the rate at
3011        which the $COMM token is permanently
3012        removed or 'burned' from circulation.
3013
3014        Returns:
3015            The burn rate for the network.
3016
3017        Raises:
3018            QueryError: If the query to the network fails or is invalid.
3019        """
3020
3021        return self.query(
3022            "BurnRate",
3023            params=[],
3024        )
3025
3026    def get_burn(self, netuid: int = 0) -> int:
3027        """
3028        Queries the network for the burn setting.
3029
3030        Retrieves the burn value, which represents the amount of the
3031        $COMM token that is 'burned' or permanently removed from
3032        circulation.
3033
3034        Args:
3035            netuid: The network UID for which to query the burn value.
3036
3037        Returns:
3038            The burn value for the specified network subnet.
3039
3040        Raises:
3041            QueryError: If the query to the network fails or is invalid.
3042        """
3043
3044        return self.query("Burn", params=[netuid])
3045
3046    def get_min_burn(self) -> int:
3047        """
3048        Queries the network for the minimum burn setting.
3049
3050        Retrieves the minimum burn value, indicating the lowest
3051        amount of the $COMM tokens that can be 'burned' or
3052        permanently removed from circulation.
3053
3054        Returns:
3055            The minimum burn value for the network.
3056
3057        Raises:
3058            QueryError: If the query to the network fails or is invalid.
3059        """
3060
3061        return self.query(
3062            "BurnConfig",
3063            params=[],
3064        )["min_burn"]
3065
3066    def get_min_weight_stake(self) -> int:
3067        """
3068        Queries the network for the minimum weight stake setting.
3069
3070        Retrieves the minimum weight stake, which represents the lowest
3071        stake weight that is allowed for certain operations or
3072        transactions within the network.
3073
3074        Returns:
3075            The minimum weight stake for the network.
3076
3077        Raises:
3078            QueryError: If the query to the network fails or is invalid.
3079        """
3080
3081        return self.query("MinWeightStake", params=[])
3082
3083    def get_vote_mode_global(self) -> str:
3084        """
3085        Queries the network for the global vote mode setting.
3086
3087        Retrieves the global vote mode, which defines the overall voting
3088        methodology or approach used across the network in default.
3089
3090        Returns:
3091            The global vote mode setting for the network.
3092
3093        Raises:
3094            QueryError: If the query to the network fails or is invalid.
3095        """
3096
3097        return self.query(
3098            "VoteModeGlobal",
3099        )
3100
3101    def get_max_proposals(self) -> int:
3102        """
3103        Queries the network for the maximum number of proposals allowed.
3104
3105        Retrieves the upper limit on the number of proposals that can be
3106        active or considered at any given time within the network.
3107
3108        Returns:
3109            The maximum number of proposals allowed on the network.
3110
3111        Raises:
3112            QueryError: If the query to the network fails or is invalid.
3113        """
3114
3115        return self.query(
3116            "MaxProposals",
3117        )
3118
3119    def get_max_registrations_per_block(self) -> int:
3120        """
3121        Queries the network for the maximum number of registrations per block.
3122
3123        Retrieves the maximum number of registrations that can
3124        be processed in each block within the network.
3125
3126        Returns:
3127            The maximum number of registrations per block on the network.
3128
3129        Raises:
3130            QueryError: If the query to the network fails or is invalid.
3131        """
3132
3133        return self.query(
3134            "MaxRegistrationsPerBlock",
3135            params=[],
3136        )
3137
3138    def get_max_name_length(self) -> int:
3139        """
3140        Queries the network for the maximum length allowed for names.
3141
3142        Retrieves the maximum character length permitted for names
3143        within the network. Such as the module names
3144
3145        Returns:
3146            The maximum length allowed for names on the network.
3147
3148        Raises:
3149            QueryError: If the query to the network fails or is invalid.
3150        """
3151
3152        return self.query(
3153            "MaxNameLength",
3154            params=[],
3155        )
3156
3157    def get_global_vote_threshold(self) -> int:
3158        """
3159        Queries the network for the global vote threshold.
3160
3161        Retrieves the global vote threshold, which is the critical value or
3162        percentage required for decisions in the network's governance process.
3163
3164        Returns:
3165            The global vote threshold for the network.
3166
3167        Raises:
3168            QueryError: If the query to the network fails or is invalid.
3169        """
3170
3171        return self.query(
3172            "GlobalVoteThreshold",
3173        )
3174
3175    def get_max_allowed_subnets(self) -> int:
3176        """
3177        Queries the network for the maximum number of allowed subnets.
3178
3179        Retrieves the upper limit on the number of subnets that can
3180        be created or operated within the network.
3181
3182        Returns:
3183            The maximum number of allowed subnets on the network.
3184
3185        Raises:
3186            QueryError: If the query to the network fails or is invalid.
3187        """
3188
3189        return self.query(
3190            "MaxAllowedSubnets",
3191            params=[],
3192        )
3193
3194    def get_max_allowed_modules(self) -> int:
3195        """
3196        Queries the network for the maximum number of allowed modules.
3197
3198        Retrieves the upper limit on the number of modules that
3199        can be registered within the network.
3200
3201        Returns:
3202            The maximum number of allowed modules on the network.
3203
3204        Raises:
3205            QueryError: If the query to the network fails or is invalid.
3206        """
3207
3208        return self.query(
3209            "MaxAllowedModules",
3210            params=[],
3211        )
3212
3213    def get_min_stake(self, netuid: int = 0) -> int:
3214        """
3215        Queries the network for the minimum stake required to register a key.
3216
3217        Retrieves the minimum amount of stake necessary for
3218        registering a key within a specific network subnet.
3219
3220        Args:
3221            netuid: The network UID for which to query the minimum stake.
3222
3223        Returns:
3224            The minimum stake required for key registration in nanos.
3225
3226        Raises:
3227            QueryError: If the query to the network fails or is invalid.
3228        """
3229
3230        return self.query("MinStake", params=[netuid])
3231
3232    def get_stakefrom(
3233        self,
3234        key: Ss58Address,
3235    ) -> dict[str, int]:
3236        """
3237        Retrieves the stake amounts from all stakers to a specific staked address.
3238
3239        Queries the network for the stakes received by a particular staked address
3240        from all stakers.
3241
3242        Args:
3243            key: The address of the key receiving the stakes.
3244
3245        Returns:
3246            A dictionary mapping staker addresses to their respective stake amounts.
3247
3248        Raises:
3249            QueryError: If the query to the network fails or is invalid.
3250        """
3251
3252        # Has to use query map in order to iterate through the storage prefix.
3253        return self.query_map("StakeFrom", [key], extract_value=False).get(
3254            "StakeFrom", {}
3255        )
3256
3257    def get_staketo(
3258        self,
3259        key: Ss58Address,
3260    ) -> dict[str, int]:
3261        """
3262        Retrieves the stake amounts provided by a specific staker to all staked addresses.
3263
3264        Queries the network for the stakes provided by a particular staker to
3265        all staked addresses.
3266
3267        Args:
3268            key: The address of the key providing the stakes.
3269
3270        Returns:
3271            A dictionary mapping staked addresses to their respective received stake amounts.
3272
3273        Raises:
3274            QueryError: If the query to the network fails or is invalid.
3275        """
3276
3277        # Has to use query map in order to iterate through the storage prefix.
3278        return self.query_map("StakeTo", [key], extract_value=False).get(
3279            "StakeTo", {}
3280        )
3281
3282    def get_balance(
3283        self,
3284        addr: Ss58Address,
3285    ) -> int:
3286        """
3287        Retrieves the balance of a specific key.
3288
3289        Args:
3290            addr: The address of the key to query the balance for.
3291
3292        Returns:
3293            The balance of the specified key.
3294
3295        Raises:
3296            QueryError: If the query to the network fails or is invalid.
3297        """
3298
3299        result = self.query("Account", module="System", params=[addr])
3300
3301        return result["data"]["free"]
3302
3303    def get_block(self, block_hash: str | None = None) -> dict[Any, Any] | None:
3304        """
3305        Retrieves information about a specific block in the network.
3306
3307        Queries the network for details about a block, such as its number,
3308        hash, and other relevant information.
3309
3310        Returns:
3311            The requested information about the block,
3312            or None if the block does not exist
3313            or the information is not available.
3314
3315        Raises:
3316            QueryError: If the query to the network fails or is invalid.
3317        """
3318
3319        with self.get_conn() as substrate:
3320            block: dict[Any, Any] | None = substrate.get_block(  # type: ignore
3321                block_hash  # type: ignore
3322            )
3323
3324        return block
3325
3326    def get_existential_deposit(self, block_hash: str | None = None) -> int:
3327        """
3328        Retrieves the existential deposit value for the network.
3329
3330        The existential deposit is the minimum balance that must be maintained
3331        in an account to prevent it from being purged. Denotated in nano units.
3332
3333        Returns:
3334            The existential deposit value in nano units.
3335        Note:
3336            The value returned is a fixed value defined in the
3337            client and may not reflect changes in the network's configuration.
3338        """
3339
3340        with self.get_conn() as substrate:
3341            result: int = substrate.get_constant(  #  type: ignore
3342                "Balances", "ExistentialDeposit", block_hash
3343            ).value  #  type: ignore
3344
3345        return result
3346
3347    def get_voting_power_delegators(self) -> list[Ss58Address]:
3348        result = self.query(
3349            "NotDelegatingVotingPower", [], module="GovernanceModule"
3350        )
3351        return result
3352
3353    def add_transfer_dao_treasury_proposal(
3354        self,
3355        key: Keypair,
3356        data: str,
3357        amount_nano: int,
3358        dest: Ss58Address,
3359    ):
3360        params = {"dest": dest, "value": amount_nano, "data": data}
3361
3362        return self.compose_call(
3363            module="GovernanceModule",
3364            fn="add_transfer_dao_treasury_proposal",
3365            params=params,
3366            key=key,
3367        )
3368
3369
3370if __name__ == "__main__":
3371    from time import sleep
3372
3373    from communex._common import get_node_url
3374    from communex.compat.key import try_classic_load_key
3375
3376    kp = try_classic_load_key("testkey")
3377    node = get_node_url(use_testnet=True)
3378    print(f"Using node: {node}")
3379    client = CommuneClient(node, timeout=65, num_connections=1)
3380    while True:
3381        timeout = 8
3382        sleep(timeout)
3383        block = client.get_block()
3384        print(block["header"]["number"])  # type: ignore
MAX_REQUEST_SIZE = 9000000
@dataclass
class ConnectionContainer:
28@dataclass
29class ConnectionContainer:
30    substrate: SubstrateInterface
31    stop_event: threading.Event
32    lock: threading.Lock
ConnectionContainer( substrate: substrateinterface.base.SubstrateInterface, stop_event: threading.Event, lock: <built-in function allocate_lock>)
substrate: substrateinterface.base.SubstrateInterface
stop_event: threading.Event
lock: <built-in function allocate_lock>
@dataclass
class Chunk:
35@dataclass
36class Chunk:
37    batch_requests: list[tuple[Any, Any]]
38    prefix_list: list[list[str]]
39    fun_params: list[tuple[Any, Any, Any, Any, str]]
Chunk( batch_requests: list[tuple[typing.Any, typing.Any]], prefix_list: list[list[str]], fun_params: list[tuple[typing.Any, typing.Any, typing.Any, typing.Any, str]])
batch_requests: list[tuple[typing.Any, typing.Any]]
prefix_list: list[list[str]]
fun_params: list[tuple[typing.Any, typing.Any, typing.Any, typing.Any, str]]
def send_heartbeat( si: substrateinterface.base.SubstrateInterface, stop: threading.Event, lock: <built-in function allocate_lock>):
46def send_heartbeat(
47    si: SubstrateInterface,
48    stop: threading.Event,
49    lock: threading.Lock,
50):
51    while not stop.is_set():
52        # uses substrateinterface wrapper because its very stateful
53        # and we could mess with something by directly using the websocket
54        with lock:
55            _ = si.websocket.pong()  # type: ignore
56        sleep(11)  # Send heartbeat every 30 seconds
class CommuneClient:
  78class CommuneClient:
  79    """
  80    A client for interacting with Commune network nodes, querying storage,
  81    submitting transactions, etc.
  82
  83    Attributes:
  84        wait_for_finalization: Whether to wait for transaction finalization.
  85
  86    Example:
  87    ```py
  88    client = CommuneClient()
  89    client.query(name='function_name', params=['param1', 'param2'])
  90    ```
  91
  92    Raises:
  93        AssertionError: If the maximum connections value is less than or equal
  94          to zero.
  95    """
  96
  97    wait_for_finalization: bool
  98    _num_connections: int
  99    _connection_queue: queue.Queue[ConnectionContainer]
 100    _ws_options: dict[str, int]
 101    url: str
 102
 103    def __init__(
 104        self,
 105        url: str,
 106        num_connections: int = 1,
 107        wait_for_finalization: bool = False,
 108        timeout: int | None = None,
 109    ):
 110        """
 111        Args:
 112            url: The URL of the network node to connect to.
 113            num_connections: The number of websocket connections to be opened.
 114        """
 115        assert num_connections > 0
 116        self._num_connections = num_connections
 117        self.wait_for_finalization = wait_for_finalization
 118        self._connection_queue = queue.Queue(num_connections)
 119        self.url = url
 120
 121        for _ in range(num_connections):
 122            ws_options: dict[str, int] = {}
 123            if timeout is not None:
 124                ws_options["timeout"] = timeout
 125            self._ws_options = ws_options
 126            self._connection_queue.put(
 127                _instantiate_substrateinterface(
 128                    url, ws_options, threading.Lock()
 129                )
 130            )
 131
 132    @property
 133    def connections(self) -> int:
 134        """
 135        Gets the maximum allowed number of simultaneous connections to the
 136        network node.
 137        """
 138        return self._num_connections
 139
 140    @contextmanager
 141    def get_conn(self, timeout: float | None = None, init: bool = False):
 142        """
 143        Context manager to get a connection from the pool.
 144
 145        Tries to get a connection from the pool queue. If the queue is empty,
 146        it blocks for `timeout` seconds until a connection is available. If
 147        `timeout` is None, it blocks indefinitely.
 148
 149        Args:
 150            timeout: The maximum time in seconds to wait for a connection.
 151
 152        Yields:
 153            The connection object from the pool.
 154
 155        Raises:
 156            QueueEmptyError: If no connection is available within the timeout
 157              period.
 158        """
 159        conn = self._connection_queue.get(timeout=timeout)
 160        if init:
 161            conn.substrate.init_runtime()  # type: ignore
 162        try:
 163            gc.collect()
 164            if conn.substrate.websocket and conn.substrate.websocket.connected:  # type: ignore
 165                with conn.lock:
 166                    yield conn.substrate
 167            else:
 168                # reconnects
 169                conn.stop_event.set()
 170                conn = _instantiate_substrateinterface(
 171                    self.url, self._ws_options, threading.Lock()
 172                )
 173                with conn.lock:
 174                    yield conn.substrate
 175        finally:
 176            self._connection_queue.put(conn)
 177
 178    def _get_storage_keys(
 179        self,
 180        storage: str,
 181        queries: list[tuple[str, list[Any]]],
 182        block_hash: str | None,
 183    ):
 184        send: list[tuple[str, list[Any]]] = []
 185        prefix_list: list[Any] = []
 186
 187        key_idx = 0
 188        with self.get_conn(init=True) as substrate:
 189            for function, params in queries:
 190                storage_key = StorageKey.create_from_storage_function(  # type: ignore
 191                    storage,
 192                    function,
 193                    params,
 194                    runtime_config=substrate.runtime_config,  # type: ignore
 195                    metadata=substrate.metadata,  # type: ignore
 196                )
 197
 198                prefix = storage_key.to_hex()
 199                prefix_list.append(prefix)
 200                send.append(("state_getKeys", [prefix, block_hash]))
 201                key_idx += 1
 202        return send, prefix_list
 203
 204    def _get_lists(
 205        self,
 206        storage_module: str,
 207        queries: list[tuple[str, list[Any]]],
 208        substrate: SubstrateInterface,
 209    ) -> list[tuple[Any, Any, Any, Any, str]]:
 210        """
 211        Generates a list of tuples containing parameters for each storage function based on the given functions and substrate interface.
 212
 213        Args:
 214            functions (dict[str, list[query_call]]): A dictionary where keys are storage module names and values are lists of tuples.
 215                Each tuple consists of a storage function name and its parameters.
 216            substrate: An instance of the SubstrateInterface class used to interact with the substrate.
 217
 218        Returns:
 219            A list of tuples in the format `(value_type, param_types, key_hashers, params, storage_function)` for each storage function in the given functions.
 220
 221        Example:
 222            >>> _get_lists(
 223                    functions={'storage_module': [('storage_function', ['param1', 'param2'])]},
 224                    substrate=substrate_instance
 225                )
 226            [('value_type', 'param_types', 'key_hashers', ['param1', 'param2'], 'storage_function'), ...]
 227        """
 228
 229        function_parameters: list[tuple[Any, Any, Any, Any, str]] = []
 230
 231        metadata_pallet = substrate.metadata.get_metadata_pallet(  # type: ignore
 232            storage_module
 233        )
 234        for storage_function, params in queries:
 235            storage_item = metadata_pallet.get_storage_function(  # type: ignore
 236                storage_function
 237            )
 238
 239            value_type = storage_item.get_value_type_string()  # type: ignore
 240            param_types = storage_item.get_params_type_string()  # type: ignore
 241            key_hashers = storage_item.get_param_hashers()  # type: ignore
 242            function_parameters.append(
 243                (
 244                    value_type,
 245                    param_types,
 246                    key_hashers,
 247                    params,
 248                    storage_function,
 249                )  # type: ignore
 250            )
 251        return function_parameters
 252
 253    def _send_batch(
 254        self,
 255        batch_payload: list[Any],
 256        request_ids: list[int],
 257        extract_result: bool = True,
 258    ):
 259        """
 260        Sends a batch of requests to the substrate and collects the results.
 261
 262        Args:
 263            substrate: An instance of the substrate interface.
 264            batch_payload: The payload of the batch request.
 265            request_ids: A list of request IDs for tracking responses.
 266            results: A list to store the results of the requests.
 267            extract_result: Whether to extract the result from the response.
 268
 269        Raises:
 270            NetworkQueryError: If there is an `error` in the response message.
 271
 272        Note:
 273            No explicit return value as results are appended to the provided 'results' list.
 274        """
 275        results: list[str | dict[Any, Any]] = []
 276        with self.get_conn(init=True) as substrate:
 277            try:
 278                substrate.websocket.send(  #  type: ignore
 279                    json.dumps(batch_payload)
 280                )
 281            except NetworkQueryError:
 282                pass
 283            while len(results) < len(request_ids):
 284                received_messages = json.loads(
 285                    substrate.websocket.recv()  # type: ignore
 286                )
 287                if isinstance(received_messages, dict):
 288                    received_messages: list[dict[Any, Any]] = [
 289                        received_messages
 290                    ]
 291
 292                for message in received_messages:
 293                    if message.get("id") in request_ids:
 294                        if extract_result:
 295                            try:
 296                                results.append(message["result"])
 297                            except Exception:
 298                                raise (
 299                                    RuntimeError(
 300                                        f"Error extracting result from message: {message}"
 301                                    )
 302                                )
 303                        else:
 304                            results.append(message)
 305                    if "error" in message:
 306                        raise NetworkQueryError(message["error"])
 307
 308            return results
 309
 310    def _make_request_smaller(
 311        self,
 312        batch_request: list[tuple[T1, T2]],
 313        prefix_list: list[list[str]],
 314        fun_params: list[tuple[Any, Any, Any, Any, str]],
 315    ) -> tuple[list[list[tuple[T1, T2]]], list[Chunk]]:
 316        """
 317        Splits a batch of requests into smaller batches, each not exceeding the specified maximum size.
 318
 319        Args:
 320            batch_request: A list of requests to be sent in a batch.
 321            max_size: Maximum size of each batch in bytes.
 322
 323        Returns:
 324            A list of smaller request batches.
 325
 326        Example:
 327            >>> _make_request_smaller(batch_request=[('method1', 'params1'), ('method2', 'params2')], max_size=1000)
 328            [[('method1', 'params1')], [('method2', 'params2')]]
 329        """
 330        assert len(prefix_list) == len(fun_params) == len(batch_request)
 331
 332        def estimate_size(request: tuple[T1, T2]):
 333            """Convert the batch request to a string and measure its length"""
 334            return len(json.dumps(request))
 335
 336        # Initialize variables
 337        result: list[list[tuple[T1, T2]]] = []
 338        current_batch = []
 339        current_prefix_batch = []
 340        current_params_batch = []
 341        current_size = 0
 342
 343        chunk_list: list[Chunk] = []
 344
 345        # Iterate through each request in the batch
 346        for request, prefix, params in zip(
 347            batch_request, prefix_list, fun_params
 348        ):
 349            request_size = estimate_size(request)
 350
 351            # Check if adding this request exceeds the max size
 352            if current_size + request_size > MAX_REQUEST_SIZE:
 353                # If so, start a new batch
 354
 355                # Essentiatly checks that it's not the first iteration
 356                if current_batch:
 357                    chunk = Chunk(
 358                        current_batch,
 359                        current_prefix_batch,
 360                        current_params_batch,
 361                    )
 362                    chunk_list.append(chunk)
 363                    result.append(current_batch)
 364
 365                current_batch = [request]
 366                current_prefix_batch = [prefix]
 367                current_params_batch = [params]
 368                current_size = request_size
 369            else:
 370                # Otherwise, add to the current batch
 371                current_batch.append(request)
 372                current_size += request_size
 373                current_prefix_batch.append(prefix)
 374                current_params_batch.append(params)
 375
 376        # Add the last batch if it's not empty
 377        if current_batch:
 378            result.append(current_batch)
 379            chunk = Chunk(
 380                current_batch, current_prefix_batch, current_params_batch
 381            )
 382            chunk_list.append(chunk)
 383
 384        return result, chunk_list
 385
 386    def _are_changes_equal(self, change_a: Any, change_b: Any):
 387        for (a, b), (c, d) in zip(change_a, change_b):
 388            if a != c or b != d:
 389                return False
 390
 391    def _rpc_request_batch(
 392        self,
 393        batch_requests: list[tuple[str, list[Any]]],
 394        extract_result: bool = True,
 395    ) -> list[str]:
 396        """
 397        Sends batch requests to the substrate node using multiple threads and collects the results.
 398
 399        Args:
 400            substrate: An instance of the substrate interface.
 401            batch_requests : A list of requests to be sent in batches.
 402            max_size: Maximum size of each batch in bytes.
 403            extract_result: Whether to extract the result from the response message.
 404
 405        Returns:
 406            A list of results from the batch requests.
 407
 408        Example:
 409            >>> _rpc_request_batch(substrate_instance, [('method1', ['param1']), ('method2', ['param2'])])
 410            ['result1', 'result2', ...]
 411        """
 412
 413        chunk_results: list[Any] = []
 414        # smaller_requests = self._make_request_smaller(batch_requests)
 415        request_id = 0
 416        with ThreadPoolExecutor() as executor:
 417            futures: list[Future[list[str | dict[Any, Any]]]] = []
 418            for chunk in [batch_requests]:
 419                request_ids: list[int] = []
 420                batch_payload: list[Any] = []
 421                for method, params in chunk:
 422                    request_id += 1
 423                    request_ids.append(request_id)
 424                    batch_payload.append(
 425                        {
 426                            "jsonrpc": "2.0",
 427                            "method": method,
 428                            "params": params,
 429                            "id": request_id,
 430                        }
 431                    )
 432
 433                futures.append(
 434                    executor.submit(
 435                        self._send_batch,
 436                        batch_payload=batch_payload,
 437                        request_ids=request_ids,
 438                        extract_result=extract_result,
 439                    )
 440                )
 441            for future in futures:
 442                resul = future.result()
 443                chunk_results.append(resul)
 444        return chunk_results
 445
 446    def _rpc_request_batch_chunked(
 447        self, chunk_requests: list[Chunk], extract_result: bool = True
 448    ):
 449        """
 450        Sends batch requests to the substrate node using multiple threads and collects the results.
 451
 452        Args:
 453            substrate: An instance of the substrate interface.
 454            batch_requests : A list of requests to be sent in batches.
 455            max_size: Maximum size of each batch in bytes.
 456            extract_result: Whether to extract the result from the response message.
 457
 458        Returns:
 459            A list of results from the batch requests.
 460
 461        Example:
 462            >>> _rpc_request_batch(substrate_instance, [('method1', ['param1']), ('method2', ['param2'])])
 463            ['result1', 'result2', ...]
 464        """
 465
 466        def split_chunks(
 467            chunk: Chunk, chunk_info: list[Chunk], chunk_info_idx: int
 468        ):
 469            manhattam_chunks: list[tuple[Any, Any]] = []
 470            mutaded_chunk_info = deepcopy(chunk_info)
 471            max_n_keys = 35000
 472            for query in chunk.batch_requests:
 473                result_keys = query[1][0]
 474                keys_amount = len(result_keys)
 475                if keys_amount > max_n_keys:
 476                    mutaded_chunk_info.pop(chunk_info_idx)
 477                    for i in range(0, keys_amount, max_n_keys):
 478                        new_chunk = deepcopy(chunk)
 479                        splitted_keys = result_keys[i : i + max_n_keys]
 480                        splitted_query = deepcopy(query)
 481                        splitted_query[1][0] = splitted_keys
 482                        new_chunk.batch_requests = [splitted_query]
 483                        manhattam_chunks.append(splitted_query)
 484                        mutaded_chunk_info.insert(chunk_info_idx, new_chunk)
 485                else:
 486                    manhattam_chunks.append(query)
 487            return manhattam_chunks, mutaded_chunk_info
 488
 489        assert len(chunk_requests) > 0
 490        mutated_chunk_info: list[Chunk] = []
 491        chunk_results: list[Any] = []
 492        # smaller_requests = self._make_request_smaller(batch_requests)
 493        request_id = 0
 494
 495        with ThreadPoolExecutor() as executor:
 496            futures: list[Future[list[str | dict[Any, Any]]]] = []
 497            for idx, macro_chunk in enumerate(chunk_requests):
 498                _, mutated_chunk_info = split_chunks(
 499                    macro_chunk, chunk_requests, idx
 500                )
 501            for chunk in mutated_chunk_info:
 502                request_ids: list[int] = []
 503                batch_payload: list[Any] = []
 504                for method, params in chunk.batch_requests:
 505                    # for method, params in micro_chunk:
 506                    request_id += 1
 507                    request_ids.append(request_id)
 508                    batch_payload.append(
 509                        {
 510                            "jsonrpc": "2.0",
 511                            "method": method,
 512                            "params": params,
 513                            "id": request_id,
 514                        }
 515                    )
 516                futures.append(
 517                    executor.submit(
 518                        self._send_batch,
 519                        batch_payload=batch_payload,
 520                        request_ids=request_ids,
 521                        extract_result=extract_result,
 522                    )
 523                )
 524            for future in futures:
 525                resul = future.result()
 526                chunk_results.append(resul)
 527        return chunk_results, mutated_chunk_info
 528
 529    def _decode_response(
 530        self,
 531        response: list[str],
 532        function_parameters: list[tuple[Any, Any, Any, Any, str]],
 533        prefix_list: list[Any],
 534        block_hash: str,
 535    ) -> dict[str, dict[Any, Any]]:
 536        """
 537        Decodes a response from the substrate interface and organizes the data into a dictionary.
 538
 539        Args:
 540            response: A list of encoded responses from a substrate query.
 541            function_parameters: A list of tuples containing the parameters for each storage function.
 542            last_keys: A list of the last keys used in the substrate query.
 543            prefix_list: A list of prefixes used in the substrate query.
 544            substrate: An instance of the SubstrateInterface class.
 545            block_hash: The hash of the block to be queried.
 546
 547        Returns:
 548            A dictionary where each key is a storage function name and the value is another dictionary.
 549            This inner dictionary's key is the decoded key from the response and the value is the corresponding decoded value.
 550
 551        Raises:
 552            ValueError: If an unsupported hash type is encountered in the `concat_hash_len` function.
 553
 554        Example:
 555            >>> _decode_response(
 556                    response=[...],
 557                    function_parameters=[...],
 558                    last_keys=[...],
 559                    prefix_list=[...],
 560                    substrate=substrate_instance,
 561                    block_hash="0x123..."
 562                )
 563            {'storage_function_name': {decoded_key: decoded_value, ...}, ...}
 564        """
 565
 566        def get_item_key_value(
 567            item_key: tuple[Any, ...] | Any,
 568        ) -> tuple[Any, ...] | Any:
 569            if isinstance(item_key, tuple):
 570                return tuple(k.value for k in item_key)  # type: ignore
 571            return item_key.value
 572
 573        def concat_hash_len(key_hasher: str) -> int:
 574            """
 575            Determines the length of the hash based on the given key hasher type.
 576
 577            Args:
 578                key_hasher: The type of key hasher.
 579
 580            Returns:
 581                The length of the hash corresponding to the given key hasher type.
 582
 583            Raises:
 584                ValueError: If the key hasher type is not supported.
 585
 586            Example:
 587                >>> concat_hash_len("Blake2_128Concat")
 588                16
 589            """
 590
 591            if key_hasher == "Blake2_128Concat":
 592                return 16
 593            elif key_hasher == "Twox64Concat":
 594                return 8
 595            elif key_hasher == "Identity":
 596                return 0
 597            else:
 598                raise ValueError("Unsupported hash type")
 599
 600        assert len(response) == len(function_parameters) == len(prefix_list)
 601        result_dict: dict[str, dict[Any, Any]] = {}
 602        for res, fun_params_tuple, prefix in zip(
 603            response, function_parameters, prefix_list
 604        ):
 605            if not res:
 606                continue
 607            res = res[0]
 608            changes = res["changes"]  # type: ignore
 609            value_type, param_types, key_hashers, params, storage_function = (
 610                fun_params_tuple
 611            )
 612            with self.get_conn(init=True) as substrate:
 613                for item in changes:
 614                    # Determine type string
 615                    key_type_string: list[Any] = []
 616                    for n in range(len(params), len(param_types)):
 617                        key_type_string.append(
 618                            f"[u8; {concat_hash_len(key_hashers[n])}]"
 619                        )
 620                        key_type_string.append(param_types[n])
 621
 622                    item_key_obj = substrate.decode_scale(  # type: ignore
 623                        type_string=f"({', '.join(key_type_string)})",
 624                        scale_bytes="0x" + item[0][len(prefix) :],
 625                        return_scale_obj=True,
 626                        block_hash=block_hash,
 627                    )
 628                    # strip key_hashers to use as item key
 629                    if len(param_types) - len(params) == 1:
 630                        item_key = item_key_obj.value_object[1]  # type: ignore
 631                    else:
 632                        item_key = tuple(  # type: ignore
 633                            item_key_obj.value_object[key + 1]  # type: ignore
 634                            for key in range(  # type: ignore
 635                                len(params), len(param_types) + 1, 2
 636                            )
 637                        )
 638
 639                    item_value = substrate.decode_scale(  # type: ignore
 640                        type_string=value_type,
 641                        scale_bytes=item[1],
 642                        return_scale_obj=True,
 643                        block_hash=block_hash,
 644                    )
 645                    result_dict.setdefault(storage_function, {})
 646                    key = get_item_key_value(item_key)  # type: ignore
 647                    result_dict[storage_function][key] = item_value.value  # type: ignore
 648
 649        return result_dict
 650
 651    def query_batch(
 652        self, functions: dict[str, list[tuple[str, list[Any]]]]
 653    ) -> dict[str, str]:
 654        """
 655        Executes batch queries on a substrate and returns results in a dictionary format.
 656
 657        Args:
 658            substrate: An instance of SubstrateInterface to interact with the substrate.
 659            functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls (function name and parameters).
 660
 661        Returns:
 662            A dictionary where keys are storage function names and values are the query results.
 663
 664        Raises:
 665            Exception: If no result is found from the batch queries.
 666
 667        Example:
 668            >>> query_batch(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
 669            {'function_name': 'query_result', ...}
 670        """
 671
 672        result: dict[str, str] = {}
 673        if not functions:
 674            raise Exception("No result")
 675        with self.get_conn(init=True) as substrate:
 676            for module, queries in functions.items():
 677                storage_keys: list[Any] = []
 678                for fn, params in queries:
 679                    storage_function = substrate.create_storage_key(  # type: ignore
 680                        pallet=module, storage_function=fn, params=params
 681                    )
 682                    storage_keys.append(storage_function)
 683
 684                block_hash = substrate.get_block_hash()
 685                responses: list[Any] = substrate.query_multi(  # type: ignore
 686                    storage_keys=storage_keys, block_hash=block_hash
 687                )
 688
 689                for item in responses:
 690                    fun = item[0]
 691                    query = item[1]
 692                    storage_fun = fun.storage_function
 693                    result[storage_fun] = query.value
 694
 695        return result
 696
 697    def query_batch_map(
 698        self,
 699        functions: dict[str, list[tuple[str, list[Any]]]],
 700        block_hash: str | None = None,
 701    ) -> dict[str, dict[Any, Any]]:
 702        """
 703        Queries multiple storage functions using a map batch approach and returns the combined result.
 704
 705        Args:
 706            substrate: An instance of SubstrateInterface for substrate interaction.
 707            functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls.
 708
 709        Returns:
 710            The combined result of the map batch query.
 711
 712        Example:
 713            >>> query_batch_map(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
 714            # Returns the combined result of the map batch query
 715        """
 716        multi_result: dict[str, dict[Any, Any]] = {}
 717
 718        def recursive_update(
 719            d: dict[str, dict[T1, T2] | dict[str, Any]],
 720            u: Mapping[str, dict[Any, Any] | str],
 721        ) -> dict[str, dict[T1, T2]]:
 722            for k, v in u.items():
 723                if isinstance(v, dict):
 724                    d[k] = recursive_update(d.get(k, {}), v)  # type: ignore
 725                else:
 726                    d[k] = v  # type: ignore
 727            return d  # type: ignore
 728
 729        def get_page():
 730            send, prefix_list = self._get_storage_keys(
 731                storage, queries, block_hash
 732            )
 733            with self.get_conn(init=True) as substrate:
 734                function_parameters = self._get_lists(
 735                    storage, queries, substrate
 736                )
 737            responses = self._rpc_request_batch(send)
 738            # assumption because send is just the storage_function keys
 739            # so it should always be really small regardless of the amount of queries
 740            assert len(responses) == 1
 741            res = responses[0]
 742            built_payload: list[tuple[str, list[Any]]] = []
 743            for result_keys in res:
 744                built_payload.append(
 745                    ("state_queryStorageAt", [result_keys, block_hash])
 746                )
 747            _, chunks_info = self._make_request_smaller(
 748                built_payload, prefix_list, function_parameters
 749            )
 750            chunks_response, chunks_info = self._rpc_request_batch_chunked(
 751                chunks_info
 752            )
 753            return chunks_response, chunks_info
 754
 755        if not block_hash:
 756            with self.get_conn(init=True) as substrate:
 757                block_hash = substrate.get_block_hash()
 758        for storage, queries in functions.items():
 759            chunks, chunks_info = get_page()
 760            # if this doesn't happen something is wrong on the code
 761            # and we won't be able to decode the data properly
 762            assert len(chunks) == len(chunks_info)
 763            for chunk_info, response in zip(chunks_info, chunks):
 764                storage_result = self._decode_response(
 765                    response,
 766                    chunk_info.fun_params,
 767                    chunk_info.prefix_list,
 768                    block_hash,
 769                )
 770                multi_result = recursive_update(multi_result, storage_result)
 771
 772        return multi_result
 773
 774    def query(
 775        self,
 776        name: str,
 777        params: list[Any] = [],
 778        module: str = "SubspaceModule",
 779        block_hash: str | None = None,
 780    ) -> Any:
 781        """
 782        Queries a storage function on the network.
 783
 784        Sends a query to the network and retrieves data from a
 785        specified storage function.
 786
 787        Args:
 788            name: The name of the storage function to query.
 789            params: The parameters to pass to the storage function.
 790            module: The module where the storage function is located.
 791
 792        Returns:
 793            The result of the query from the network.
 794
 795        Raises:
 796            NetworkQueryError: If the query fails or is invalid.
 797        """
 798
 799        result = self.query_batch({module: [(name, params)]})
 800
 801        return result[name]
 802
 803    def query_map(
 804        self,
 805        name: str,
 806        params: list[Any] = [],
 807        module: str = "SubspaceModule",
 808        extract_value: bool = True,
 809        block_hash: str | None = None,
 810    ) -> dict[Any, Any]:
 811        """
 812        Queries a storage map from a network node.
 813
 814        Args:
 815            name: The name of the storage map to query.
 816            params: A list of parameters for the query.
 817            module: The module in which the storage map is located.
 818
 819        Returns:
 820            A dictionary representing the key-value pairs
 821              retrieved from the storage map.
 822
 823        Raises:
 824            QueryError: If the query to the network fails or is invalid.
 825        """
 826
 827        result = self.query_batch_map({module: [(name, params)]}, block_hash)
 828
 829        if extract_value:
 830            return {k.value: v.value for k, v in result}  # type: ignore
 831
 832        return result
 833
 834    def compose_call(
 835        self,
 836        fn: str,
 837        params: dict[str, Any],
 838        key: Keypair | None,
 839        module: str = "SubspaceModule",
 840        wait_for_inclusion: bool = True,
 841        wait_for_finalization: bool | None = None,
 842        sudo: bool = False,
 843        unsigned: bool = False,
 844    ) -> ExtrinsicReceipt:
 845        """
 846        Composes and submits a call to the network node.
 847
 848        Composes and signs a call with the provided keypair, and submits it to
 849        the network. The call can be a standard extrinsic or a sudo extrinsic if
 850        elevated permissions are required. The method can optionally wait for
 851        the call's inclusion in a block and/or its finalization.
 852
 853        Args:
 854            fn: The function name to call on the network.
 855            params: A dictionary of parameters for the call.
 856            key: The keypair for signing the extrinsic.
 857            module: The module containing the function.
 858            wait_for_inclusion: Wait for the call's inclusion in a block.
 859            wait_for_finalization: Wait for the transaction's finalization.
 860            sudo: Execute the call as a sudo (superuser) operation.
 861
 862        Returns:
 863            The receipt of the submitted extrinsic, if
 864              `wait_for_inclusion` is True. Otherwise, returns a string
 865              identifier of the extrinsic.
 866
 867        Raises:
 868            ChainTransactionError: If the transaction fails.
 869        """
 870
 871        if key is None and not unsigned:
 872            raise ValueError("Key must be provided for signed extrinsics.")
 873
 874        with self.get_conn() as substrate:
 875            if wait_for_finalization is None:
 876                wait_for_finalization = self.wait_for_finalization
 877
 878            call = substrate.compose_call(  # type: ignore
 879                call_module=module, call_function=fn, call_params=params
 880            )
 881            if sudo:
 882                call = substrate.compose_call(  # type: ignore
 883                    call_module="Sudo",
 884                    call_function="sudo",
 885                    call_params={
 886                        "call": call.value,  # type: ignore
 887                    },
 888                )
 889
 890            if not unsigned:
 891                assert key is not None
 892                extrinsic = substrate.create_signed_extrinsic(  # type: ignore
 893                    call=call,
 894                    keypair=key,
 895                )
 896            else:
 897                extrinsic = substrate.create_unsigned_extrinsic(call=call)  # type: ignore
 898
 899            response = substrate.submit_extrinsic(
 900                extrinsic=extrinsic,
 901                wait_for_inclusion=wait_for_inclusion,
 902                wait_for_finalization=wait_for_finalization,
 903            )
 904        if wait_for_inclusion:
 905            if not response.is_success:
 906                raise ChainTransactionError(
 907                    response.error_message,  # type: ignore
 908                    response,  # type: ignore
 909                )
 910
 911        return response
 912
 913    def compose_call_multisig(
 914        self,
 915        fn: str,
 916        params: dict[str, Any],
 917        key: Keypair,
 918        signatories: list[Ss58Address],
 919        threshold: int,
 920        module: str = "SubspaceModule",
 921        wait_for_inclusion: bool = True,
 922        wait_for_finalization: bool | None = None,
 923        sudo: bool = False,
 924        era: dict[str, int] | None = None,
 925    ) -> ExtrinsicReceipt:
 926        """
 927        Composes and submits a multisignature call to the network node.
 928
 929        This method allows the composition and submission of a call that
 930        requires multiple signatures for execution, known as a multisignature
 931        call. It supports specifying signatories, a threshold of signatures for
 932        the call's execution, and an optional era for the call's mortality. The
 933        call can be a standard extrinsic, a sudo extrinsic for elevated
 934        permissions, or a multisig extrinsic if multiple signatures are
 935        required. Optionally, the method can wait for the call's inclusion in a
 936        block and/or its finalization. Make sure to pass all keys,
 937        that are part of the multisignature.
 938
 939        Args:
 940            fn: The function name to call on the network. params: A dictionary
 941            of parameters for the call. key: The keypair for signing the
 942            extrinsic. signatories: List of SS58 addresses of the signatories.
 943            Include ALL KEYS that are part of the multisig. threshold: The
 944            minimum number of signatories required to execute the extrinsic.
 945            module: The module containing the function to call.
 946            wait_for_inclusion: Whether to wait for the call's inclusion in a
 947            block. wait_for_finalization: Whether to wait for the transaction's
 948            finalization. sudo: Execute the call as a sudo (superuser)
 949            operation. era: Specifies the call's mortality in terms of blocks in
 950            the format
 951                {'period': amount_blocks}. If omitted, the extrinsic is
 952                immortal.
 953
 954        Returns:
 955            The receipt of the submitted extrinsic if `wait_for_inclusion` is
 956            True. Otherwise, returns a string identifier of the extrinsic.
 957
 958        Raises:
 959            ChainTransactionError: If the transaction fails.
 960        """
 961
 962        # getting the call ready
 963        with self.get_conn() as substrate:
 964            if wait_for_finalization is None:
 965                wait_for_finalization = self.wait_for_finalization
 966
 967            # prepares the `GenericCall` object
 968            call = substrate.compose_call(  # type: ignore
 969                call_module=module, call_function=fn, call_params=params
 970            )
 971            if sudo:
 972                call = substrate.compose_call(  # type: ignore
 973                    call_module="Sudo",
 974                    call_function="sudo",
 975                    call_params={
 976                        "call": call.value,  # type: ignore
 977                    },
 978                )
 979
 980            # modify the rpc methods at runtime, to allow for correct payment
 981            # fee calculation parity has a bug in this version,
 982            # where the method has to be removed
 983            rpc_methods = substrate.config.get("rpc_methods")  # type: ignore
 984
 985            if "state_call" in rpc_methods:  # type: ignore
 986                rpc_methods.remove("state_call")  # type: ignore
 987
 988            # create the multisig account
 989            multisig_acc = substrate.generate_multisig_account(  # type: ignore
 990                signatories, threshold
 991            )
 992
 993            # send the multisig extrinsic
 994            extrinsic = substrate.create_multisig_extrinsic(  # type: ignore
 995                call=call,  # type: ignore
 996                keypair=key,
 997                multisig_account=multisig_acc,  # type: ignore
 998                era=era,  # type: ignore
 999            )  # type: ignore
1000
1001            response = substrate.submit_extrinsic(
1002                extrinsic=extrinsic,
1003                wait_for_inclusion=wait_for_inclusion,
1004                wait_for_finalization=wait_for_finalization,
1005            )
1006
1007        if wait_for_inclusion:
1008            if not response.is_success:
1009                raise ChainTransactionError(
1010                    response.error_message,  # type: ignore
1011                    response,  # type: ignore
1012                )
1013
1014        return response
1015
1016    def transfer(
1017        self,
1018        key: Keypair,
1019        amount: int,
1020        dest: Ss58Address,
1021    ) -> ExtrinsicReceipt:
1022        """
1023        Transfers a specified amount of tokens from the signer's account to the
1024        specified account.
1025
1026        Args:
1027            key: The keypair associated with the sender's account.
1028            amount: The amount to transfer, in nanotokens.
1029            dest: The SS58 address of the recipient.
1030
1031        Returns:
1032            A receipt of the transaction.
1033
1034        Raises:
1035            InsufficientBalanceError: If the sender's account does not have
1036              enough balance.
1037            ChainTransactionError: If the transaction fails.
1038        """
1039
1040        params = {"dest": dest, "value": amount}
1041
1042        return self.compose_call(
1043            module="Balances", fn="transfer_keep_alive", params=params, key=key
1044        )
1045
1046    def transfer_multiple(
1047        self,
1048        key: Keypair,
1049        destinations: list[Ss58Address],
1050        amounts: list[int],
1051        netuid: str | int = 0,
1052    ) -> ExtrinsicReceipt:
1053        """
1054        Transfers specified amounts of tokens from the signer's account to
1055        multiple target accounts.
1056
1057        The `destinations` and `amounts` lists must be of the same length.
1058
1059        Args:
1060            key: The keypair associated with the sender's account.
1061            destinations: A list of SS58 addresses of the recipients.
1062            amounts: Amount to transfer to each recipient, in nanotokens.
1063            netuid: The network identifier.
1064
1065        Returns:
1066            A receipt of the transaction.
1067
1068        Raises:
1069            InsufficientBalanceError: If the sender's account does not have
1070              enough balance for all transfers.
1071            ChainTransactionError: If the transaction fails.
1072        """
1073
1074        assert len(destinations) == len(amounts)
1075
1076        # extract existential deposit from amounts
1077        existential_deposit = self.get_existential_deposit()
1078        amounts = [a - existential_deposit for a in amounts]
1079
1080        params = {
1081            "netuid": netuid,
1082            "destinations": destinations,
1083            "amounts": amounts,
1084        }
1085
1086        return self.compose_call(
1087            module="SubspaceModule",
1088            fn="transfer_multiple",
1089            params=params,
1090            key=key,
1091        )
1092
1093    def stake(
1094        self,
1095        key: Keypair,
1096        amount: int,
1097        dest: Ss58Address,
1098    ) -> ExtrinsicReceipt:
1099        """
1100        Stakes the specified amount of tokens to a module key address.
1101
1102        Args:
1103            key: The keypair associated with the staker's account.
1104            amount: The amount of tokens to stake, in nanotokens.
1105            dest: The SS58 address of the module key to stake to.
1106            netuid: The network identifier.
1107
1108        Returns:
1109            A receipt of the staking transaction.
1110
1111        Raises:
1112            InsufficientBalanceError: If the staker's account does not have
1113              enough balance.
1114            ChainTransactionError: If the transaction fails.
1115        """
1116
1117        params = {"amount": amount, "module_key": dest}
1118
1119        return self.compose_call(fn="add_stake", params=params, key=key)
1120
1121    def unstake(
1122        self,
1123        key: Keypair,
1124        amount: int,
1125        dest: Ss58Address,
1126    ) -> ExtrinsicReceipt:
1127        """
1128        Unstakes the specified amount of tokens from a module key address.
1129
1130        Args:
1131            key: The keypair associated with the unstaker's account.
1132            amount: The amount of tokens to unstake, in nanotokens.
1133            dest: The SS58 address of the module key to unstake from.
1134            netuid: The network identifier.
1135
1136        Returns:
1137            A receipt of the unstaking transaction.
1138
1139        Raises:
1140            InsufficientStakeError: If the staked key does not have enough
1141              staked tokens by the signer key.
1142            ChainTransactionError: If the transaction fails.
1143        """
1144
1145        params = {"amount": amount, "module_key": dest}
1146        return self.compose_call(fn="remove_stake", params=params, key=key)
1147
1148    def update_module(
1149        self,
1150        key: Keypair,
1151        name: str,
1152        address: str,
1153        metadata: str | None = None,
1154        delegation_fee: int = 20,
1155        weight_setting_delegation_fee: int | None = None,
1156        netuid: int = 0,
1157    ) -> ExtrinsicReceipt:
1158        """
1159        Updates the parameters of a registered module.
1160
1161        The delegation fee must be an integer between 0 and 100.
1162
1163        Args:
1164            key: The keypair associated with the module's account.
1165            name: The new name for the module. If None, the name is not updated.
1166            address: The new address for the module.
1167                If None, the address is not updated.
1168            delegation_fee: The new delegation fee for the module,
1169                between 0 and 100.
1170            netuid: The network identifier.
1171
1172        Returns:
1173            A receipt of the module update transaction.
1174
1175        Raises:
1176            InvalidParameterError: If the provided parameters are invalid.
1177            ChainTransactionError: If the transaction fails.
1178        """
1179
1180        assert isinstance(delegation_fee, int)
1181        params = {
1182            "netuid": netuid,
1183            "name": name,
1184            "address": address,
1185            "stake_delegation_fee": delegation_fee,
1186            "validator_weight_fee": weight_setting_delegation_fee,
1187            "metadata": metadata,
1188        }
1189
1190        response = self.compose_call("update_module", params=params, key=key)
1191
1192        return response
1193
1194    def register_module(
1195        self,
1196        key: Keypair,
1197        name: str,
1198        address: str | None = None,
1199        subnet: str = "Rootnet",
1200        metadata: str | None = None,
1201    ) -> ExtrinsicReceipt:
1202        """
1203        Registers a new module in the network.
1204
1205        Args:
1206            key: The keypair used for registering the module.
1207            name: The name of the module. If None, a default or previously
1208                set name is used. # How does this work?
1209            address: The address of the module. If None, a default or
1210                previously set address is used. # How does this work?
1211            subnet: The network subnet to register the module in.
1212            min_stake: The minimum stake required for the module, in nanotokens.
1213                If None, a default value is used.
1214
1215        Returns:
1216            A receipt of the registration transaction.
1217
1218        Raises:
1219            InvalidParameterError: If the provided parameters are invalid.
1220            ChainTransactionError: If the transaction fails.
1221        """
1222
1223        key_addr = key.ss58_address
1224
1225        params = {
1226            "network_name": subnet,
1227            "address": address,
1228            "name": name,
1229            "module_key": key_addr,
1230            "metadata": metadata,
1231        }
1232
1233        response = self.compose_call("register", params=params, key=key)
1234        return response
1235
1236    def deregister_module(self, key: Keypair, netuid: int) -> ExtrinsicReceipt:
1237        """
1238        Deregisters a module from the network.
1239
1240        Args:
1241            key: The keypair associated with the module's account.
1242            netuid: The network identifier.
1243
1244        Returns:
1245            A receipt of the module deregistration transaction.
1246
1247        Raises:
1248            ChainTransactionError: If the transaction fails.
1249        """
1250
1251        params = {"netuid": netuid}
1252
1253        response = self.compose_call("deregister", params=params, key=key)
1254
1255        return response
1256
1257    def register_subnet(
1258        self, key: Keypair, name: str, metadata: str | None = None
1259    ) -> ExtrinsicReceipt:
1260        """
1261        Registers a new subnet in the network.
1262
1263        Args:
1264            key (Keypair): The keypair used for registering the subnet.
1265            name (str): The name of the subnet to be registered.
1266            metadata (str | None, optional): Additional metadata for the subnet. Defaults to None.
1267
1268        Returns:
1269            ExtrinsicReceipt: A receipt of the subnet registration transaction.
1270
1271        Raises:
1272            ChainTransactionError: If the transaction fails.
1273        """
1274
1275        params = {
1276            "name": name,
1277            "metadata": metadata,
1278        }
1279
1280        response = self.compose_call("register_subnet", params=params, key=key)
1281
1282        return response
1283
1284    def vote(
1285        self,
1286        key: Keypair,
1287        uids: list[int],
1288        weights: list[int],
1289        netuid: int = 0,
1290    ) -> ExtrinsicReceipt:
1291        """
1292        Casts votes on a list of module UIDs with corresponding weights.
1293
1294        The length of the UIDs list and the weights list should be the same.
1295        Each weight corresponds to the UID at the same index.
1296
1297        Args:
1298            key: The keypair used for signing the vote transaction.
1299            uids: A list of module UIDs to vote on.
1300            weights: A list of weights corresponding to each UID.
1301            netuid: The network identifier.
1302
1303        Returns:
1304            A receipt of the voting transaction.
1305
1306        Raises:
1307            InvalidParameterError: If the lengths of UIDs and weights lists
1308                do not match.
1309            ChainTransactionError: If the transaction fails.
1310        """
1311
1312        assert len(uids) == len(weights)
1313
1314        params = {
1315            "uids": uids,
1316            "weights": weights,
1317            "netuid": netuid,
1318        }
1319
1320        response = self.compose_call(
1321            "set_weights",
1322            params=params,
1323            key=key,
1324            module="SubnetEmissionModule",
1325        )
1326
1327        return response
1328
1329    def delegate_weight_control(
1330        self, key: Keypair, target: Ss58Address, netuid: int
1331    ):
1332        """
1333        delegates weight setting control from the current account to the target account.
1334        Both accounts have to be registered and have a valid validator `spec`
1335        """
1336
1337        params = {
1338            "netuid": netuid,
1339            "target": target,
1340        }
1341
1342        response = self.compose_call(
1343            "delegate_weight_control",
1344            params=params,
1345            key=key,
1346            module="SubnetEmissionModule",
1347        )
1348        return response
1349
1350    def add_authorities(
1351        self,
1352        key: Keypair,
1353        new_authorities: list[tuple[Ss58Address, tuple[str, str]]],
1354    ):
1355        """
1356        only for the sudo key
1357        give the authorities as hexadecimal
1358        """
1359        params = {
1360            "new_authorities": new_authorities,
1361        }
1362        response = self.compose_call(
1363            "add_authorities",
1364            params=params,
1365            key=key,
1366            module="Offworker",
1367            sudo=True,
1368        )
1369        return response
1370
1371    def vote_encrypted(
1372        self,
1373        key: Keypair,
1374        uids: list[int],
1375        weights: list[int],
1376        netuid: int = 0,
1377    ) -> ExtrinsicReceipt:
1378        """
1379        Casts encrypted votes for module UIDs with corresponding weights.
1380
1381        Args:
1382            key (Keypair): The keypair used for signing the transaction.
1383            uids (list[int]): List of UIDs to vote for.
1384            weights (list[int]): List of weights corresponding to each UID.
1385            netuid (int, optional): Network UID. Defaults to 0.
1386
1387        Returns:
1388            ExtrinsicReceipt: Receipt of the submitted extrinsic.
1389
1390        Raises:
1391            ValueError: If there's a length mismatch between UIDs and weights,
1392                if subnet data is not found, or if subnet key format is invalid.
1393        """
1394        if len(uids) != len(weights):
1395            raise ValueError("Length mismatch between UIDs and weights")
1396
1397        subnet_data = self.query(
1398            "SubnetDecryptionData",
1399            module="SubnetEmissionModule",
1400            params=[netuid],
1401        )
1402        if not subnet_data:
1403            raise ValueError("Subnet data not found")
1404
1405        subnet_key = subnet_data.get("node_public_key")
1406        if not subnet_key or len(subnet_key) < 2:
1407            raise ValueError("Invalid subnet key format")
1408
1409        vote_data = list(zip(uids, weights))
1410        decryptor = (
1411            bytes_from_hex(subnet_key[0]),
1412            bytes_from_hex(subnet_key[1]),
1413        )
1414        validator_key = [int(x) for x in key.public_key]
1415
1416        encrypted_weights = encrypt_weights(decryptor, vote_data, validator_key)
1417        weights_hash = hashlib.sha256(str(weights).encode()).hexdigest()
1418
1419        params = {
1420            "uids": uids,
1421            "encrypted_weights": encrypted_weights,
1422            "netuid": netuid,
1423            "decrypted_weights_hash": weights_hash,
1424        }
1425
1426        return self.compose_call(
1427            "set_weights_encrypted",
1428            params=params,
1429            key=key,
1430            module="SubnetEmissionModule",
1431        )
1432
1433    def update_subnet(
1434        self,
1435        key: Keypair,
1436        params: SubnetParams,
1437        netuid: int = 0,
1438    ) -> ExtrinsicReceipt:
1439        """
1440        Update a subnet's configuration.
1441
1442        It requires the founder key for authorization.
1443
1444        Args:
1445            key: The founder keypair of the subnet.
1446            params: The new parameters for the subnet.
1447            netuid: The network identifier.
1448
1449        Returns:
1450            A receipt of the subnet update transaction.
1451
1452        Raises:
1453            AuthorizationError: If the key is not authorized.
1454            ChainTransactionError: If the transaction fails.
1455        """
1456
1457        general_params = dict(params)
1458        general_params["netuid"] = netuid
1459        if general_params.get("subnet_metadata") is None:
1460            general_params["metadata"] = None
1461        else:
1462            general_params["metadata"] = general_params["subnet_metadata"]
1463
1464        response = self.compose_call(
1465            fn="update_subnet",
1466            params=general_params,
1467            key=key,
1468        )
1469
1470        return response
1471
1472    def transfer_stake(
1473        self,
1474        key: Keypair,
1475        amount: int,
1476        from_module_key: Ss58Address,
1477        dest_module_address: Ss58Address,
1478    ) -> ExtrinsicReceipt:
1479        """
1480        Realocate staked tokens from one staked module to another module.
1481
1482        Args:
1483            key: The keypair associated with the account that is delegating the tokens.
1484            amount: The amount of staked tokens to transfer, in nanotokens.
1485            from_module_key: The SS58 address of the module you want to transfer from (currently delegated by the key).
1486            dest_module_address: The SS58 address of the destination (newly delegated key).
1487            netuid: The network identifier.
1488
1489        Returns:
1490            A receipt of the stake transfer transaction.
1491
1492        Raises:
1493            InsufficientStakeError: If the source module key does not have
1494            enough staked tokens. ChainTransactionError: If the transaction
1495            fails.
1496        """
1497
1498        amount = amount - self.get_existential_deposit()
1499
1500        params = {
1501            "amount": amount,
1502            "module_key": from_module_key,
1503            "new_module_key": dest_module_address,
1504        }
1505
1506        response = self.compose_call("transfer_stake", key=key, params=params)
1507
1508        return response
1509
1510    def bridge(
1511        self,
1512        key: Keypair,
1513        amount: int,
1514    ):
1515        """
1516        Bridge tokens from the Subspace network to the Torus network.
1517
1518        Args:
1519            key: The keypair associated with the account that is bridging the tokens.
1520            amount: The amount of tokens to bridge, in nanotokens.
1521
1522        Returns:
1523            A receipt of the bridging transaction.
1524
1525        Raises:
1526            InsufficientBalanceError: If the account does not have enough balance.
1527            ChainTransactionError: If the transaction fails.
1528        """
1529
1530        params = {"amount": amount}
1531
1532        response = self.compose_call("bridge", key=key, params=params)
1533
1534        return response
1535
1536    def bridge_withdraw(
1537        self,
1538        key: Keypair,
1539        amount: int,
1540    ):
1541        """
1542        Withdraw bridged tokens from the Torus network to the Subspace network.
1543
1544        Args:
1545            key: The keypair associated with the account that is withdrawing the tokens.
1546            amount: The amount of tokens to withdraw, in nanotokens.
1547
1548        Returns:
1549            A receipt of the withdrawal transaction.
1550
1551        Raises:
1552            InsufficientBalanceError: If the account does not have enough balance.
1553            ChainTransactionError: If the transaction fails.
1554        """
1555
1556        params = {"amount": amount}
1557
1558        response = self.compose_call("bridge_withdraw", key=key, params=params)
1559
1560        return response
1561
1562    def multiunstake(
1563        self,
1564        key: Keypair,
1565        keys: list[Ss58Address],
1566        amounts: list[int],
1567    ) -> ExtrinsicReceipt:
1568        """
1569        Unstakes tokens from multiple module keys.
1570
1571        And the lists `keys` and `amounts` must be of the same length. Each
1572        amount corresponds to the module key at the same index.
1573
1574        Args:
1575            key: The keypair associated with the unstaker's account.
1576            keys: A list of SS58 addresses of the module keys to unstake from.
1577            amounts: A list of amounts to unstake from each module key,
1578              in nanotokens.
1579            netuid: The network identifier.
1580
1581        Returns:
1582            A receipt of the multi-unstaking transaction.
1583
1584        Raises:
1585            MismatchedLengthError: If the lengths of keys and amounts lists do
1586            not match. InsufficientStakeError: If any of the module keys do not
1587            have enough staked tokens. ChainTransactionError: If the transaction
1588            fails.
1589        """
1590
1591        assert len(keys) == len(amounts)
1592
1593        params = {"module_keys": keys, "amounts": amounts}
1594
1595        response = self.compose_call(
1596            "remove_stake_multiple", params=params, key=key
1597        )
1598
1599        return response
1600
1601    def multistake(
1602        self,
1603        key: Keypair,
1604        keys: list[Ss58Address],
1605        amounts: list[int],
1606    ) -> ExtrinsicReceipt:
1607        """
1608        Stakes tokens to multiple module keys.
1609
1610        The lengths of the `keys` and `amounts` lists must be the same. Each
1611        amount corresponds to the module key at the same index.
1612
1613        Args:
1614            key: The keypair associated with the staker's account.
1615            keys: A list of SS58 addresses of the module keys to stake to.
1616            amounts: A list of amounts to stake to each module key,
1617                in nanotokens.
1618            netuid: The network identifier.
1619
1620        Returns:
1621            A receipt of the multi-staking transaction.
1622
1623        Raises:
1624            MismatchedLengthError: If the lengths of keys and amounts lists
1625                do not match.
1626            ChainTransactionError: If the transaction fails.
1627        """
1628
1629        assert len(keys) == len(amounts)
1630
1631        params = {
1632            "module_keys": keys,
1633            "amounts": amounts,
1634        }
1635
1636        response = self.compose_call(
1637            "add_stake_multiple", params=params, key=key
1638        )
1639
1640        return response
1641
1642    def add_profit_shares(
1643        self,
1644        key: Keypair,
1645        keys: list[Ss58Address],
1646        shares: list[int],
1647    ) -> ExtrinsicReceipt:
1648        """
1649        Allocates profit shares to multiple keys.
1650
1651        The lists `keys` and `shares` must be of the same length,
1652        with each share amount corresponding to the key at the same index.
1653
1654        Args:
1655            key: The keypair associated with the account
1656                distributing the shares.
1657            keys: A list of SS58 addresses to allocate shares to.
1658            shares: A list of share amounts to allocate to each key,
1659                in nanotokens.
1660
1661        Returns:
1662            A receipt of the profit sharing transaction.
1663
1664        Raises:
1665            MismatchedLengthError: If the lengths of keys and shares
1666                lists do not match.
1667            ChainTransactionError: If the transaction fails.
1668        """
1669
1670        assert len(keys) == len(shares)
1671
1672        params = {"keys": keys, "shares": shares}
1673
1674        response = self.compose_call(
1675            "add_profit_shares", params=params, key=key
1676        )
1677
1678        return response
1679
1680    def add_subnet_proposal(
1681        self, key: Keypair, params: dict[str, Any], ipfs: str, netuid: int = 0
1682    ) -> ExtrinsicReceipt:
1683        """
1684        Submits a proposal for creating or modifying a subnet within the
1685        network.
1686
1687        The proposal includes various parameters like the name, founder, share
1688        allocations, and other subnet-specific settings.
1689
1690        Args:
1691            key: The keypair used for signing the proposal transaction.
1692            params: The parameters for the subnet proposal.
1693            netuid: The network identifier.
1694
1695        Returns:
1696            A receipt of the subnet proposal transaction.
1697
1698        Raises:
1699            InvalidParameterError: If the provided subnet
1700                parameters are invalid.
1701            ChainTransactionError: If the transaction fails.
1702        """
1703
1704        general_params = dict(params)
1705        general_params["netuid"] = netuid
1706        general_params["data"] = ipfs
1707        if "metadata" not in general_params:
1708            general_params["metadata"] = None
1709
1710        # general_params["burn_config"] = json.dumps(general_params["burn_config"])
1711        response = self.compose_call(
1712            fn="add_subnet_params_proposal",
1713            params=general_params,
1714            key=key,
1715            module="GovernanceModule",
1716        )
1717
1718        return response
1719
1720    def add_custom_proposal(
1721        self,
1722        key: Keypair,
1723        cid: str,
1724    ) -> ExtrinsicReceipt:
1725        params = {"data": cid}
1726
1727        response = self.compose_call(
1728            fn="add_global_custom_proposal",
1729            params=params,
1730            key=key,
1731            module="GovernanceModule",
1732        )
1733        return response
1734
1735    def add_custom_subnet_proposal(
1736        self,
1737        key: Keypair,
1738        cid: str,
1739        netuid: int = 0,
1740    ) -> ExtrinsicReceipt:
1741        """
1742        Submits a proposal for creating or modifying a custom subnet within the
1743        network.
1744
1745        The proposal includes various parameters like the name, founder, share
1746        allocations, and other subnet-specific settings.
1747
1748        Args:
1749            key: The keypair used for signing the proposal transaction.
1750            params: The parameters for the subnet proposal.
1751            netuid: The network identifier.
1752
1753        Returns:
1754            A receipt of the subnet proposal transaction.
1755        """
1756
1757        params = {
1758            "data": cid,
1759            "netuid": netuid,
1760        }
1761
1762        response = self.compose_call(
1763            fn="add_subnet_custom_proposal",
1764            params=params,
1765            key=key,
1766            module="GovernanceModule",
1767        )
1768
1769        return response
1770
1771    def add_global_proposal(
1772        self,
1773        key: Keypair,
1774        params: NetworkParams,
1775        cid: str | None,
1776    ) -> ExtrinsicReceipt:
1777        """
1778        Submits a proposal for altering the global network parameters.
1779
1780        Allows for the submission of a proposal to
1781        change various global parameters
1782        of the network, such as emission rates, rate limits, and voting
1783        thresholds. It is used to
1784        suggest changes that affect the entire network's operation.
1785
1786        Args:
1787            key: The keypair used for signing the proposal transaction.
1788            params: A dictionary containing global network parameters
1789                    like maximum allowed subnets, modules,
1790                    transaction rate limits, and others.
1791
1792        Returns:
1793            A receipt of the global proposal transaction.
1794
1795        Raises:
1796            InvalidParameterError: If the provided network
1797                parameters are invalid.
1798            ChainTransactionError: If the transaction fails.
1799        """
1800        general_params = cast(dict[str, Any], params)
1801        cid = cid or ""
1802        general_params["data"] = cid
1803
1804        response = self.compose_call(
1805            fn="add_global_params_proposal",
1806            params=general_params,
1807            key=key,
1808            module="GovernanceModule",
1809        )
1810
1811        return response
1812
1813    def vote_on_proposal(
1814        self,
1815        key: Keypair,
1816        proposal_id: int,
1817        agree: bool,
1818    ) -> ExtrinsicReceipt:
1819        """
1820        Casts a vote on a specified proposal within the network.
1821
1822        Args:
1823            key: The keypair used for signing the vote transaction.
1824            proposal_id: The unique identifier of the proposal to vote on.
1825
1826        Returns:
1827            A receipt of the voting transaction in nanotokens.
1828
1829        Raises:
1830            InvalidProposalIDError: If the provided proposal ID does not
1831                exist or is invalid.
1832            ChainTransactionError: If the transaction fails.
1833        """
1834
1835        params = {"proposal_id": proposal_id, "agree": agree}
1836
1837        response = self.compose_call(
1838            "vote_proposal",
1839            key=key,
1840            params=params,
1841            module="GovernanceModule",
1842        )
1843
1844        return response
1845
1846    def unvote_on_proposal(
1847        self,
1848        key: Keypair,
1849        proposal_id: int,
1850    ) -> ExtrinsicReceipt:
1851        """
1852        Retracts a previously cast vote on a specified proposal.
1853
1854        Args:
1855            key: The keypair used for signing the unvote transaction.
1856            proposal_id: The unique identifier of the proposal to withdraw the
1857                vote from.
1858
1859        Returns:
1860            A receipt of the unvoting transaction in nanotokens.
1861
1862        Raises:
1863            InvalidProposalIDError: If the provided proposal ID does not
1864                exist or is invalid.
1865            ChainTransactionError: If the transaction fails to be processed, or
1866                if there was no prior vote to retract.
1867        """
1868
1869        params = {"proposal_id": proposal_id}
1870
1871        response = self.compose_call(
1872            "remove_vote_proposal",
1873            key=key,
1874            params=params,
1875            module="GovernanceModule",
1876        )
1877
1878        return response
1879
1880    def enable_vote_power_delegation(self, key: Keypair) -> ExtrinsicReceipt:
1881        """
1882        Enables vote power delegation for the signer's account.
1883
1884        Args:
1885            key: The keypair used for signing the delegation transaction.
1886
1887        Returns:
1888            A receipt of the vote power delegation transaction.
1889
1890        Raises:
1891            ChainTransactionError: If the transaction fails.
1892        """
1893
1894        response = self.compose_call(
1895            "enable_vote_power_delegation",
1896            params={},
1897            key=key,
1898            module="GovernanceModule",
1899        )
1900
1901        return response
1902
1903    def disable_vote_power_delegation(self, key: Keypair) -> ExtrinsicReceipt:
1904        """
1905        Disables vote power delegation for the signer's account.
1906
1907        Args:
1908            key: The keypair used for signing the delegation transaction.
1909
1910        Returns:
1911            A receipt of the vote power delegation transaction.
1912
1913        Raises:
1914            ChainTransactionError: If the transaction fails.
1915        """
1916
1917        response = self.compose_call(
1918            "disable_vote_power_delegation",
1919            params={},
1920            key=key,
1921            module="GovernanceModule",
1922        )
1923
1924        return response
1925
1926    def add_dao_application(
1927        self, key: Keypair, application_key: Ss58Address, data: str
1928    ) -> ExtrinsicReceipt:
1929        """
1930        Submits a new application to the general subnet DAO.
1931
1932        Args:
1933            key: The keypair used for signing the application transaction.
1934            application_key: The SS58 address of the application key.
1935            data: The data associated with the application.
1936
1937        Returns:
1938            A receipt of the application transaction.
1939
1940        Raises:
1941            ChainTransactionError: If the transaction fails.
1942        """
1943
1944        params = {"application_key": application_key, "data": data}
1945
1946        response = self.compose_call(
1947            "add_dao_application",
1948            module="GovernanceModule",
1949            key=key,
1950            params=params,
1951        )
1952
1953        return response
1954
1955    def query_map_curator_applications(self) -> dict[str, dict[str, str]]:
1956        query_result = self.query_map(
1957            "CuratorApplications",
1958            module="GovernanceModule",
1959            params=[],
1960            extract_value=False,
1961        )
1962        applications = query_result.get("CuratorApplications", {})
1963        return applications
1964
1965    def query_map_proposals(
1966        self, extract_value: bool = False
1967    ) -> dict[int, dict[str, Any]]:
1968        """
1969        Retrieves a mappping of proposals from the network.
1970
1971        Queries the network and returns a mapping of proposal IDs to
1972        their respective parameters.
1973
1974        Returns:
1975            A dictionary mapping proposal IDs
1976            to dictionaries of their parameters.
1977
1978        Raises:
1979            QueryError: If the query to the network fails or is invalid.
1980        """
1981
1982        return self.query_map(
1983            "Proposals", extract_value=extract_value, module="GovernanceModule"
1984        )["Proposals"]
1985
1986    def query_map_weights(
1987        self, netuid: int = 0, extract_value: bool = False
1988    ) -> dict[int, list[tuple[int, int]]] | None:
1989        """
1990        Retrieves a mapping of weights for keys on the network.
1991
1992        Queries the network and returns a mapping of key UIDs to
1993        their respective weights.
1994
1995        Args:
1996            netuid: The network UID from which to get the weights.
1997
1998        Returns:
1999            A dictionary mapping key UIDs to lists of their weights.
2000
2001        Raises:
2002            QueryError: If the query to the network fails or is invalid.
2003        """
2004
2005        weights_dict = self.query_map(
2006            "Weights",
2007            [netuid],
2008            extract_value=extract_value,
2009            module="SubnetEmissionModule",
2010        ).get("Weights")
2011        return weights_dict
2012
2013    def query_map_key(
2014        self,
2015        netuid: int = 0,
2016        extract_value: bool = False,
2017    ) -> dict[int, Ss58Address]:
2018        """
2019        Retrieves a map of keys from the network.
2020
2021        Fetches a mapping of key UIDs to their associated
2022        addresses on the network.
2023        The query can be targeted at a specific network UID if required.
2024
2025        Args:
2026            netuid: The network UID from which to get the keys.
2027
2028        Returns:
2029            A dictionary mapping key UIDs to their addresses.
2030
2031        Raises:
2032            QueryError: If the query to the network fails or is invalid.
2033        """
2034        return self.query_map("Keys", [netuid], extract_value=extract_value)[
2035            "Keys"
2036        ]
2037
2038    def query_map_address(
2039        self, netuid: int = 0, extract_value: bool = False
2040    ) -> dict[int, str]:
2041        """
2042        Retrieves a map of key addresses from the network.
2043
2044        Queries the network for a mapping of key UIDs to their addresses.
2045
2046        Args:
2047            netuid: The network UID from which to get the addresses.
2048
2049        Returns:
2050            A dictionary mapping key UIDs to their addresses.
2051
2052        Raises:
2053            QueryError: If the query to the network fails or is invalid.
2054        """
2055
2056        return self.query_map("Address", [netuid], extract_value=extract_value)[
2057            "Address"
2058        ]
2059
2060    def query_map_emission(
2061        self, extract_value: bool = False
2062    ) -> dict[int, list[int]]:
2063        """
2064        Retrieves a map of emissions for keys on the network.
2065
2066        Queries the network to get a mapping of
2067        key UIDs to their emission values.
2068
2069        Returns:
2070            A dictionary mapping key UIDs to lists of their emission values.
2071
2072        Raises:
2073            QueryError: If the query to the network fails or is invalid.
2074        """
2075
2076        return self.query_map("Emission", extract_value=extract_value)[
2077            "Emission"
2078        ]
2079
2080    def query_map_pending_emission(self, extract_value: bool = False) -> int:
2081        """
2082        Retrieves a map of pending emissions for the subnets.
2083
2084        Queries the network for a mapping of subnet UIDs to their pending emission values.
2085
2086        Returns:
2087            A dictionary mapping subnet UIDs to their pending emission values.
2088
2089        Raises:
2090            QueryError: If the query to the network fails or is invalid.
2091        """
2092        return self.query_map(
2093            "PendingEmission",
2094            extract_value=extract_value,
2095            module="SubnetEmissionModule",
2096        )["PendingEmission"]
2097
2098    def query_map_subnet_emission(
2099        self, extract_value: bool = False
2100    ) -> dict[int, int]:
2101        """
2102        Retrieves a map of subnet emissions for the network.
2103
2104        Queries the network for a mapping of subnet UIDs to their emission values.
2105
2106        Returns:
2107            A dictionary mapping subnet UIDs to their emission values.
2108
2109        Raises:
2110            QueryError: If the query to the network fails or is invalid.
2111        """
2112
2113        return self.query_map(
2114            "SubnetEmission",
2115            extract_value=extract_value,
2116            module="SubnetEmissionModule",
2117        )["SubnetEmission"]
2118
2119    def query_map_subnet_consensus(
2120        self, extract_value: bool = False
2121    ) -> dict[int, str]:
2122        """
2123        Retrieves a map of subnet consensus types for the network.
2124
2125        Queries the network for a mapping of subnet UIDs to their consensus types.
2126
2127        Returns:
2128            A dictionary mapping subnet UIDs to their consensus types.
2129
2130        Raises:
2131            QueryError: If the query to the network fails or is invalid.
2132        """
2133
2134        return self.query_map(
2135            "SubnetConsensusType",
2136            extract_value=extract_value,
2137            module="SubnetEmissionModule",
2138        )["SubnetConsensusType"]
2139
2140    def query_map_incentive(
2141        self, extract_value: bool = False
2142    ) -> dict[int, list[int]]:
2143        """
2144        Retrieves a mapping of incentives for keys on the network.
2145
2146        Queries the network and returns a mapping of key UIDs to
2147        their respective incentive values.
2148
2149        Returns:
2150            A dictionary mapping key UIDs to lists of their incentive values.
2151
2152        Raises:
2153            QueryError: If the query to the network fails or is invalid.
2154        """
2155
2156        return self.query_map("Incentive", extract_value=extract_value)[
2157            "Incentive"
2158        ]
2159
2160    def query_map_dividend(
2161        self, extract_value: bool = False
2162    ) -> dict[int, list[int]]:
2163        """
2164        Retrieves a mapping of dividends for keys on the network.
2165
2166        Queries the network for a mapping of key UIDs to
2167        their dividend values.
2168
2169        Returns:
2170            A dictionary mapping key UIDs to lists of their dividend values.
2171
2172        Raises:
2173            QueryError: If the query to the network fails or is invalid.
2174        """
2175
2176        return self.query_map("Dividends", extract_value=extract_value)[
2177            "Dividends"
2178        ]
2179
2180    def query_map_regblock(
2181        self, netuid: int = 0, extract_value: bool = False
2182    ) -> dict[int, int]:
2183        """
2184        Retrieves a mapping of registration blocks for keys on the network.
2185
2186        Queries the network for a mapping of key UIDs to
2187        the blocks where they were registered.
2188
2189        Args:
2190            netuid: The network UID from which to get the registration blocks.
2191
2192        Returns:
2193            A dictionary mapping key UIDs to their registration blocks.
2194
2195        Raises:
2196            QueryError: If the query to the network fails or is invalid.
2197        """
2198
2199        return self.query_map(
2200            "RegistrationBlock", [netuid], extract_value=extract_value
2201        )["RegistrationBlock"]
2202
2203    def query_map_lastupdate(
2204        self, extract_value: bool = False
2205    ) -> dict[int, list[int]]:
2206        """
2207        Retrieves a mapping of the last update times for keys on the network.
2208
2209        Queries the network for a mapping of key UIDs to their last update times.
2210
2211        Returns:
2212            A dictionary mapping key UIDs to lists of their last update times.
2213
2214        Raises:
2215            QueryError: If the query to the network fails or is invalid.
2216        """
2217
2218        return self.query_map("LastUpdate", extract_value=extract_value)[
2219            "LastUpdate"
2220        ]
2221
2222    def query_map_stakefrom(
2223        self, extract_value: bool = False
2224    ) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
2225        """
2226        Retrieves a mapping of stakes from various sources for keys on the network.
2227
2228        Queries the network to obtain a mapping of key addresses to the sources
2229        and amounts of stakes they have received.
2230
2231        Args:
2232            netuid: The network UID from which to get the stakes.
2233
2234        Returns:
2235            A dictionary mapping key addresses to lists of tuples
2236            (module_key_address, amount).
2237
2238        Raises:
2239            QueryError: If the query to the network fails or is invalid.
2240        """
2241
2242        result = self.query_map("StakeFrom", [], extract_value=extract_value)[
2243            "StakeFrom"
2244        ]
2245
2246        return transform_stake_dmap(result)
2247
2248    def query_map_staketo(
2249        self, extract_value: bool = False
2250    ) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
2251        """
2252        Retrieves a mapping of stakes to destinations for keys on the network.
2253
2254        Queries the network for a mapping of key addresses to the destinations
2255        and amounts of stakes they have made.
2256
2257        Args:
2258            netuid: The network UID from which to get the stakes.
2259
2260        Returns:
2261            A dictionary mapping key addresses to lists of tuples
2262            (module_key_address, amount).
2263
2264        Raises:
2265            QueryError: If the query to the network fails or is invalid.
2266        """
2267
2268        result = self.query_map("StakeTo", [], extract_value=extract_value)[
2269            "StakeTo"
2270        ]
2271        return transform_stake_dmap(result)
2272
2273    def query_map_delegationfee(
2274        self, netuid: int = 0, extract_value: bool = False
2275    ) -> dict[str, int]:
2276        """
2277        Retrieves a mapping of delegation fees for keys on the network.
2278
2279        Queries the network to obtain a mapping of key addresses to their
2280        respective delegation fees.
2281
2282        Args:
2283            netuid: The network UID to filter the delegation fees.
2284
2285        Returns:
2286            A dictionary mapping key addresses to their delegation fees.
2287
2288        Raises:
2289            QueryError: If the query to the network fails or is invalid.
2290        """
2291
2292        return self.query_map(
2293            "DelegationFee", [netuid], extract_value=extract_value
2294        )["DelegationFee"]
2295
2296    def query_map_tempo(self, extract_value: bool = False) -> dict[int, int]:
2297        """
2298        Retrieves a mapping of tempo settings for the network.
2299
2300        Queries the network to obtain the tempo (rate of reward distributions)
2301        settings for various network subnets.
2302
2303        Returns:
2304            A dictionary mapping network UIDs to their tempo settings.
2305
2306        Raises:
2307            QueryError: If the query to the network fails or is invalid.
2308        """
2309
2310        return self.query_map("Tempo", extract_value=extract_value)["Tempo"]
2311
2312    def query_map_immunity_period(self, extract_value: bool) -> dict[int, int]:
2313        """
2314        Retrieves a mapping of immunity periods for the network.
2315
2316        Queries the network for the immunity period settings,
2317        which represent the time duration during which modules
2318        can not get deregistered.
2319
2320        Returns:
2321            A dictionary mapping network UIDs to their immunity period settings.
2322
2323        Raises:
2324            QueryError: If the query to the network fails or is invalid.
2325        """
2326
2327        return self.query_map("ImmunityPeriod", extract_value=extract_value)[
2328            "ImmunityPeriod"
2329        ]
2330
2331    def query_map_min_allowed_weights(
2332        self, extract_value: bool = False
2333    ) -> dict[int, int]:
2334        """
2335        Retrieves a mapping of minimum allowed weights for the network.
2336
2337        Queries the network to obtain the minimum allowed weights,
2338        which are the lowest permissible weight values that can be set by
2339        validators.
2340
2341        Returns:
2342            A dictionary mapping network UIDs to
2343            their minimum allowed weight values.
2344
2345        Raises:
2346            QueryError: If the query to the network fails or is invalid.
2347        """
2348
2349        return self.query_map("MinAllowedWeights", extract_value=extract_value)[
2350            "MinAllowedWeights"
2351        ]
2352
2353    def query_map_max_allowed_weights(
2354        self, extract_value: bool = False
2355    ) -> dict[int, int]:
2356        """
2357        Retrieves a mapping of maximum allowed weights for the network.
2358
2359        Queries the network for the maximum allowed weights,
2360        which are the highest permissible
2361        weight values that can be set by validators.
2362
2363        Returns:
2364            A dictionary mapping network UIDs to
2365            their maximum allowed weight values.
2366
2367        Raises:
2368            QueryError: If the query to the network fails or is invalid.
2369        """
2370
2371        return self.query_map("MaxAllowedWeights", extract_value=extract_value)[
2372            "MaxAllowedWeights"
2373        ]
2374
2375    def query_map_max_allowed_uids(
2376        self, extract_value: bool = False
2377    ) -> dict[int, int]:
2378        """
2379        Queries the network for the maximum number of allowed user IDs (UIDs)
2380        for each network subnet.
2381
2382        Fetches a mapping of network subnets to their respective
2383        limits on the number of user IDs that can be created or used.
2384
2385        Returns:
2386            A dictionary mapping network UIDs (unique identifiers) to their
2387            maximum allowed number of UIDs.
2388            Each entry represents a network subnet
2389            with its corresponding UID limit.
2390
2391        Raises:
2392            QueryError: If the query to the network fails or is invalid.
2393        """
2394
2395        return self.query_map("MaxAllowedUids", extract_value=extract_value)[
2396            "MaxAllowedUids"
2397        ]
2398
2399    def query_map_min_stake(
2400        self, extract_value: bool = False
2401    ) -> dict[int, int]:
2402        """
2403        Retrieves a mapping of minimum allowed stake on the network.
2404
2405        Queries the network to obtain the minimum number of stake,
2406        which is represented in nanotokens.
2407
2408        Returns:
2409            A dictionary mapping network UIDs to
2410            their minimum allowed stake values.
2411
2412        Raises:
2413            QueryError: If the query to the network fails or is invalid.
2414        """
2415
2416        return self.query_map("MinStake", extract_value=extract_value)[
2417            "MinStake"
2418        ]
2419
2420    def query_map_max_stake(
2421        self, extract_value: bool = False
2422    ) -> dict[int, int]:
2423        """
2424        Retrieves a mapping of the maximum stake values for the network.
2425
2426        Queries the network for the maximum stake values across various s
2427        ubnets of the network.
2428
2429        Returns:
2430            A dictionary mapping network UIDs to their maximum stake values.
2431
2432        Raises:
2433            QueryError: If the query to the network fails or is invalid.
2434        """
2435
2436        return self.query_map("MaxStake", extract_value=extract_value)[
2437            "MaxStake"
2438        ]
2439
2440    def query_map_founder(self, extract_value: bool = False) -> dict[int, str]:
2441        """
2442        Retrieves a mapping of founders for the network.
2443
2444        Queries the network to obtain the founders associated with
2445        various subnets.
2446
2447        Returns:
2448            A dictionary mapping network UIDs to their respective founders.
2449
2450        Raises:
2451            QueryError: If the query to the network fails or is invalid.
2452        """
2453
2454        return self.query_map("Founder", extract_value=extract_value)["Founder"]
2455
2456    def query_map_founder_share(
2457        self, extract_value: bool = False
2458    ) -> dict[int, int]:
2459        """
2460        Retrieves a mapping of founder shares for the network.
2461
2462        Queries the network for the share percentages
2463        allocated to founders across different subnets.
2464
2465        Returns:
2466            A dictionary mapping network UIDs to their founder share percentages.
2467
2468        Raises:
2469            QueryError: If the query to the network fails or is invalid.
2470        """
2471
2472        return self.query_map("FounderShare", extract_value=extract_value)[
2473            "FounderShare"
2474        ]
2475
2476    def query_map_incentive_ratio(
2477        self, extract_value: bool = False
2478    ) -> dict[int, int]:
2479        """
2480        Retrieves a mapping of incentive ratios for the network.
2481
2482        Queries the network for the incentive ratios,
2483        which are the proportions of rewards or incentives
2484        allocated in different subnets of the network.
2485
2486        Returns:
2487            A dictionary mapping network UIDs to their incentive ratios.
2488
2489        Raises:
2490            QueryError: If the query to the network fails or is invalid.
2491        """
2492
2493        return self.query_map("IncentiveRatio", extract_value=extract_value)[
2494            "IncentiveRatio"
2495        ]
2496
2497    def query_map_trust_ratio(
2498        self, extract_value: bool = False
2499    ) -> dict[int, int]:
2500        """
2501        Retrieves a mapping of trust ratios for the network.
2502
2503        Queries the network for trust ratios,
2504        indicative of the level of trust or credibility assigned
2505        to different subnets of the network.
2506
2507        Returns:
2508            A dictionary mapping network UIDs to their trust ratios.
2509
2510        Raises:
2511            QueryError: If the query to the network fails or is invalid.
2512        """
2513
2514        return self.query_map("TrustRatio", extract_value=extract_value)[
2515            "TrustRatio"
2516        ]
2517
2518    def query_map_vote_mode_subnet(
2519        self, extract_value: bool = False
2520    ) -> dict[int, str]:
2521        """
2522        Retrieves a mapping of vote modes for subnets within the network.
2523
2524        Queries the network for the voting modes used in different
2525        subnets, which define the methodology or approach of voting within those
2526        subnets.
2527
2528        Returns:
2529            A dictionary mapping network UIDs to their vote
2530            modes for subnets.
2531
2532        Raises:
2533            QueryError: If the query to the network fails or is invalid.
2534        """
2535
2536        return self.query_map("VoteModeSubnet", extract_value=extract_value)[
2537            "VoteModeSubnet"
2538        ]
2539
2540    def query_map_legit_whitelist(
2541        self, extract_value: bool = False
2542    ) -> dict[Ss58Address, int]:
2543        """
2544        Retrieves a mapping of whitelisted addresses for the network.
2545
2546        Queries the network for a mapping of whitelisted addresses
2547        and their respective legitimacy status.
2548
2549        Returns:
2550            A dictionary mapping addresses to their legitimacy status.
2551
2552        Raises:
2553            QueryError: If the query to the network fails or is invalid.
2554        """
2555
2556        return self.query_map(
2557            "LegitWhitelist",
2558            module="GovernanceModule",
2559            extract_value=extract_value,
2560        )["LegitWhitelist"]
2561
2562    def query_map_subnet_names(
2563        self, extract_value: bool = False
2564    ) -> dict[int, str]:
2565        """
2566        Retrieves a mapping of subnet names within the network.
2567
2568        Queries the network for the names of various subnets,
2569        providing an overview of the different
2570        subnets within the network.
2571
2572        Returns:
2573            A dictionary mapping network UIDs to their subnet names.
2574
2575        Raises:
2576            QueryError: If the query to the network fails or is invalid.
2577        """
2578
2579        return self.query_map("SubnetNames", extract_value=extract_value)[
2580            "SubnetNames"
2581        ]
2582
2583    def query_map_balances(
2584        self, extract_value: bool = False, block_hash: str | None = None
2585    ) -> dict[str, dict[str, int | dict[str, int | float]]]:
2586        """
2587        Retrieves a mapping of account balances within the network.
2588
2589        Queries the network for the balances associated with different accounts.
2590        It provides detailed information including various types of
2591        balances for each account.
2592
2593        Returns:
2594            A dictionary mapping account addresses to their balance details.
2595
2596        Raises:
2597            QueryError: If the query to the network fails or is invalid.
2598        """
2599
2600        return self.query_map(
2601            "Account",
2602            module="System",
2603            extract_value=extract_value,
2604            block_hash=block_hash,
2605        )["Account"]
2606
2607    def query_map_registration_blocks(
2608        self, netuid: int = 0, extract_value: bool = False
2609    ) -> dict[int, int]:
2610        """
2611        Retrieves a mapping of registration blocks for UIDs on the network.
2612
2613        Queries the network to find the block numbers at which various
2614        UIDs were registered.
2615
2616        Args:
2617            netuid: The network UID from which to get the registrations.
2618
2619        Returns:
2620            A dictionary mapping UIDs to their registration block numbers.
2621
2622        Raises:
2623            QueryError: If the query to the network fails or is invalid.
2624        """
2625
2626        return self.query_map(
2627            "RegistrationBlock", [netuid], extract_value=extract_value
2628        )["RegistrationBlock"]
2629
2630    def query_map_name(
2631        self, netuid: int = 0, extract_value: bool = False
2632    ) -> dict[int, str]:
2633        """
2634        Retrieves a mapping of names for keys on the network.
2635
2636        Queries the network for the names associated with different keys.
2637        It provides a mapping of key UIDs to their registered names.
2638
2639        Args:
2640            netuid: The network UID from which to get the names.
2641
2642        Returns:
2643            A dictionary mapping key UIDs to their names.
2644
2645        Raises:
2646            QueryError: If the query to the network fails or is invalid.
2647        """
2648
2649        return self.query_map("Name", [netuid], extract_value=extract_value)[
2650            "Name"
2651        ]
2652
2653    #  == QUERY FUNCTIONS == #
2654
2655    def get_immunity_period(self, netuid: int = 0) -> int:
2656        """
2657        Queries the network for the immunity period setting.
2658
2659        The immunity period is a time duration during which a module
2660        can not be deregistered from the network.
2661        Fetches the immunity period for a specified network subnet.
2662
2663        Args:
2664            netuid: The network UID for which to query the immunity period.
2665
2666        Returns:
2667            The immunity period setting for the specified network subnet.
2668
2669        Raises:
2670            QueryError: If the query to the network fails or is invalid.
2671        """
2672
2673        return self.query(
2674            "ImmunityPeriod",
2675            params=[netuid],
2676        )
2677
2678    def get_max_set_weights_per_epoch(self):
2679        return self.query("MaximumSetWeightCallsPerEpoch")
2680
2681    def get_min_allowed_weights(self, netuid: int = 0) -> int:
2682        """
2683        Queries the network for the minimum allowed weights setting.
2684
2685        Retrieves the minimum weight values that are possible to set
2686        by a validator within a specific network subnet.
2687
2688        Args:
2689            netuid: The network UID for which to query the minimum allowed
2690              weights.
2691
2692        Returns:
2693            The minimum allowed weight values for the specified network
2694              subnet.
2695
2696        Raises:
2697            QueryError: If the query to the network fails or is invalid.
2698        """
2699
2700        return self.query(
2701            "MinAllowedWeights",
2702            params=[netuid],
2703        )
2704
2705    def get_dao_treasury_address(self) -> Ss58Address:
2706        return self.query("DaoTreasuryAddress", module="GovernanceModule")
2707
2708    def get_max_allowed_weights(self, netuid: int = 0) -> int:
2709        """
2710        Queries the network for the maximum allowed weights setting.
2711
2712        Retrieves the maximum weight values that are possible to set
2713        by a validator within a specific network subnet.
2714
2715        Args:
2716            netuid: The network UID for which to query the maximum allowed
2717              weights.
2718
2719        Returns:
2720            The maximum allowed weight values for the specified network
2721              subnet.
2722
2723        Raises:
2724            QueryError: If the query to the network fails or is invalid.
2725        """
2726
2727        return self.query("MaxAllowedWeights", params=[netuid])
2728
2729    def get_max_allowed_uids(self, netuid: int = 0) -> int:
2730        """
2731        Queries the network for the maximum allowed UIDs setting.
2732
2733        Fetches the upper limit on the number of user IDs that can
2734        be allocated or used within a specific network subnet.
2735
2736        Args:
2737            netuid: The network UID for which to query the maximum allowed UIDs.
2738
2739        Returns:
2740            The maximum number of allowed UIDs for the specified network subnet.
2741
2742        Raises:
2743            QueryError: If the query to the network fails or is invalid.
2744        """
2745
2746        return self.query("MaxAllowedUids", params=[netuid])
2747
2748    def get_name(self, netuid: int = 0) -> str:
2749        """
2750        Queries the network for the name of a specific subnet.
2751
2752        Args:
2753            netuid: The network UID for which to query the name.
2754
2755        Returns:
2756            The name of the specified network subnet.
2757
2758        Raises:
2759            QueryError: If the query to the network fails or is invalid.
2760        """
2761
2762        return self.query("Name", params=[netuid])
2763
2764    def get_subnet_name(self, netuid: int = 0) -> str:
2765        """
2766        Queries the network for the name of a specific subnet.
2767
2768        Args:
2769            netuid: The network UID for which to query the name.
2770
2771        Returns:
2772            The name of the specified network subnet.
2773
2774        Raises:
2775            QueryError: If the query to the network fails or is invalid.
2776        """
2777
2778        return self.query("SubnetNames", params=[netuid])
2779
2780    def get_global_dao_treasury(self):
2781        return self.query("GlobalDaoTreasury", module="GovernanceModule")
2782
2783    def get_n(self, netuid: int = 0) -> int:
2784        """
2785        Queries the network for the 'N' hyperparameter, which represents how
2786        many modules are on the network.
2787
2788        Args:
2789            netuid: The network UID for which to query the 'N' hyperparameter.
2790
2791        Returns:
2792            The value of the 'N' hyperparameter for the specified network
2793              subnet.
2794
2795        Raises:
2796            QueryError: If the query to the network fails or is invalid.
2797        """
2798
2799        return self.query("N", params=[netuid])
2800
2801    def get_tempo(self, netuid: int = 0) -> int:
2802        """
2803        Queries the network for the tempo setting, measured in blocks, for the
2804        specified subnet.
2805
2806        Args:
2807            netuid: The network UID for which to query the tempo.
2808
2809        Returns:
2810            The tempo setting for the specified subnet.
2811
2812        Raises:
2813            QueryError: If the query to the network fails or is invalid.
2814        """
2815
2816        return self.query("Tempo", params=[netuid])
2817
2818    def get_total_free_issuance(self, block_hash: str | None = None) -> int:
2819        """
2820        Queries the network for the total free issuance.
2821
2822        Fetches the total amount of free issuance tokens available
2823
2824        Returns:
2825            The total free issuance amount.
2826
2827        Raises:
2828            QueryError: If the query to the network fails or is invalid.
2829        """
2830
2831        return self.query(
2832            "TotalIssuance", module="Balances", block_hash=block_hash
2833        )
2834
2835    def get_total_stake(self, block_hash: str | None = None) -> int:
2836        """
2837        Retrieves a mapping of total stakes for keys on the network.
2838
2839        Queries the network for a mapping of key UIDs to their total stake amounts.
2840
2841        Returns:
2842            A dictionary mapping key UIDs to their total stake amounts.
2843
2844        Raises:
2845            QueryError: If the query to the network fails or is invalid.
2846        """
2847
2848        return self.query("TotalStake", block_hash=block_hash)
2849
2850    def get_registrations_per_block(self):
2851        """
2852        Queries the network for the number of registrations per block.
2853
2854        Fetches the number of registrations that are processed per
2855        block within the network.
2856
2857        Returns:
2858            The number of registrations processed per block.
2859
2860        Raises:
2861            QueryError: If the query to the network fails or is invalid.
2862        """
2863
2864        return self.query(
2865            "RegistrationsPerBlock",
2866        )
2867
2868    def max_registrations_per_block(self, netuid: int = 0):
2869        """
2870        Queries the network for the maximum number of registrations per block.
2871
2872        Retrieves the upper limit of registrations that can be processed in
2873        each block within a specific network subnet.
2874
2875        Args:
2876            netuid: The network UID for which to query.
2877
2878        Returns:
2879            The maximum number of registrations per block for
2880            the specified network subnet.
2881
2882        Raises:
2883            QueryError: If the query to the network fails or is invalid.
2884        """
2885
2886        return self.query(
2887            "MaxRegistrationsPerBlock",
2888            params=[netuid],
2889        )
2890
2891    def get_proposal(self, proposal_id: int = 0):
2892        """
2893        Queries the network for a specific proposal.
2894
2895        Args:
2896            proposal_id: The ID of the proposal to query.
2897
2898        Returns:
2899            The details of the specified proposal.
2900
2901        Raises:
2902            QueryError: If the query to the network fails, is invalid,
2903                or if the proposal ID does not exist.
2904        """
2905
2906        return self.query(
2907            "Proposals",
2908            params=[proposal_id],
2909        )
2910
2911    def get_trust(self, netuid: int = 0):
2912        """
2913        Queries the network for the trust setting of a specific network subnet.
2914
2915        Retrieves the trust level or score, which may represent the
2916        level of trustworthiness or reliability within a
2917        particular network subnet.
2918
2919        Args:
2920            netuid: The network UID for which to query the trust setting.
2921
2922        Returns:
2923            The trust level or score for the specified network subnet.
2924
2925        Raises:
2926            QueryError: If the query to the network fails or is invalid.
2927        """
2928
2929        return self.query(
2930            "Trust",
2931            params=[netuid],
2932        )
2933
2934    def get_uids(self, key: Ss58Address, netuid: int = 0) -> bool | None:
2935        """
2936        Queries the network for module UIDs associated with a specific key.
2937
2938        Args:
2939            key: The key address for which to query UIDs.
2940            netuid: The network UID within which to search for the key.
2941
2942        Returns:
2943            A list of UIDs associated with the specified key.
2944
2945        Raises:
2946            QueryError: If the query to the network fails or is invalid.
2947        """
2948
2949        return self.query(
2950            "Uids",
2951            params=[netuid, key],
2952        )
2953
2954    def get_unit_emission(self) -> int:
2955        """
2956        Queries the network for the unit emission setting.
2957
2958        Retrieves the unit emission value, which represents the
2959        emission rate or quantity for the $COMM token.
2960
2961        Returns:
2962            The unit emission value in nanos for the network.
2963
2964        Raises:
2965            QueryError: If the query to the network fails or is invalid.
2966        """
2967
2968        return self.query("UnitEmission", module="SubnetEmissionModule")
2969
2970    def get_tx_rate_limit(self) -> int:
2971        """
2972        Queries the network for the transaction rate limit.
2973
2974        Retrieves the rate limit for transactions within the network,
2975        which defines the maximum number of transactions that can be
2976        processed within a certain timeframe.
2977
2978        Returns:
2979            The transaction rate limit for the network.
2980
2981        Raises:
2982            QueryError: If the query to the network fails or is invalid.
2983        """
2984
2985        return self.query(
2986            "TxRateLimit",
2987        )
2988
2989    def get_subnet_burn(self) -> int:
2990        """Queries the network for the subnet burn value.
2991
2992        Retrieves the subnet burn value from the network, which represents
2993        the amount of tokens that are burned (permanently removed from
2994        circulation) for subnet-related operations.
2995
2996        Returns:
2997            int: The subnet burn value.
2998
2999        Raises:
3000            QueryError: If the query to the network fails or returns invalid data.
3001        """
3002
3003        return self.query(
3004            "SubnetBurn",
3005        )
3006
3007    def get_burn_rate(self) -> int:
3008        """
3009        Queries the network for the burn rate setting.
3010
3011        Retrieves the burn rate, which represents the rate at
3012        which the $COMM token is permanently
3013        removed or 'burned' from circulation.
3014
3015        Returns:
3016            The burn rate for the network.
3017
3018        Raises:
3019            QueryError: If the query to the network fails or is invalid.
3020        """
3021
3022        return self.query(
3023            "BurnRate",
3024            params=[],
3025        )
3026
3027    def get_burn(self, netuid: int = 0) -> int:
3028        """
3029        Queries the network for the burn setting.
3030
3031        Retrieves the burn value, which represents the amount of the
3032        $COMM token that is 'burned' or permanently removed from
3033        circulation.
3034
3035        Args:
3036            netuid: The network UID for which to query the burn value.
3037
3038        Returns:
3039            The burn value for the specified network subnet.
3040
3041        Raises:
3042            QueryError: If the query to the network fails or is invalid.
3043        """
3044
3045        return self.query("Burn", params=[netuid])
3046
3047    def get_min_burn(self) -> int:
3048        """
3049        Queries the network for the minimum burn setting.
3050
3051        Retrieves the minimum burn value, indicating the lowest
3052        amount of the $COMM tokens that can be 'burned' or
3053        permanently removed from circulation.
3054
3055        Returns:
3056            The minimum burn value for the network.
3057
3058        Raises:
3059            QueryError: If the query to the network fails or is invalid.
3060        """
3061
3062        return self.query(
3063            "BurnConfig",
3064            params=[],
3065        )["min_burn"]
3066
3067    def get_min_weight_stake(self) -> int:
3068        """
3069        Queries the network for the minimum weight stake setting.
3070
3071        Retrieves the minimum weight stake, which represents the lowest
3072        stake weight that is allowed for certain operations or
3073        transactions within the network.
3074
3075        Returns:
3076            The minimum weight stake for the network.
3077
3078        Raises:
3079            QueryError: If the query to the network fails or is invalid.
3080        """
3081
3082        return self.query("MinWeightStake", params=[])
3083
3084    def get_vote_mode_global(self) -> str:
3085        """
3086        Queries the network for the global vote mode setting.
3087
3088        Retrieves the global vote mode, which defines the overall voting
3089        methodology or approach used across the network in default.
3090
3091        Returns:
3092            The global vote mode setting for the network.
3093
3094        Raises:
3095            QueryError: If the query to the network fails or is invalid.
3096        """
3097
3098        return self.query(
3099            "VoteModeGlobal",
3100        )
3101
3102    def get_max_proposals(self) -> int:
3103        """
3104        Queries the network for the maximum number of proposals allowed.
3105
3106        Retrieves the upper limit on the number of proposals that can be
3107        active or considered at any given time within the network.
3108
3109        Returns:
3110            The maximum number of proposals allowed on the network.
3111
3112        Raises:
3113            QueryError: If the query to the network fails or is invalid.
3114        """
3115
3116        return self.query(
3117            "MaxProposals",
3118        )
3119
3120    def get_max_registrations_per_block(self) -> int:
3121        """
3122        Queries the network for the maximum number of registrations per block.
3123
3124        Retrieves the maximum number of registrations that can
3125        be processed in each block within the network.
3126
3127        Returns:
3128            The maximum number of registrations per block on the network.
3129
3130        Raises:
3131            QueryError: If the query to the network fails or is invalid.
3132        """
3133
3134        return self.query(
3135            "MaxRegistrationsPerBlock",
3136            params=[],
3137        )
3138
3139    def get_max_name_length(self) -> int:
3140        """
3141        Queries the network for the maximum length allowed for names.
3142
3143        Retrieves the maximum character length permitted for names
3144        within the network. Such as the module names
3145
3146        Returns:
3147            The maximum length allowed for names on the network.
3148
3149        Raises:
3150            QueryError: If the query to the network fails or is invalid.
3151        """
3152
3153        return self.query(
3154            "MaxNameLength",
3155            params=[],
3156        )
3157
3158    def get_global_vote_threshold(self) -> int:
3159        """
3160        Queries the network for the global vote threshold.
3161
3162        Retrieves the global vote threshold, which is the critical value or
3163        percentage required for decisions in the network's governance process.
3164
3165        Returns:
3166            The global vote threshold for the network.
3167
3168        Raises:
3169            QueryError: If the query to the network fails or is invalid.
3170        """
3171
3172        return self.query(
3173            "GlobalVoteThreshold",
3174        )
3175
3176    def get_max_allowed_subnets(self) -> int:
3177        """
3178        Queries the network for the maximum number of allowed subnets.
3179
3180        Retrieves the upper limit on the number of subnets that can
3181        be created or operated within the network.
3182
3183        Returns:
3184            The maximum number of allowed subnets on the network.
3185
3186        Raises:
3187            QueryError: If the query to the network fails or is invalid.
3188        """
3189
3190        return self.query(
3191            "MaxAllowedSubnets",
3192            params=[],
3193        )
3194
3195    def get_max_allowed_modules(self) -> int:
3196        """
3197        Queries the network for the maximum number of allowed modules.
3198
3199        Retrieves the upper limit on the number of modules that
3200        can be registered within the network.
3201
3202        Returns:
3203            The maximum number of allowed modules on the network.
3204
3205        Raises:
3206            QueryError: If the query to the network fails or is invalid.
3207        """
3208
3209        return self.query(
3210            "MaxAllowedModules",
3211            params=[],
3212        )
3213
3214    def get_min_stake(self, netuid: int = 0) -> int:
3215        """
3216        Queries the network for the minimum stake required to register a key.
3217
3218        Retrieves the minimum amount of stake necessary for
3219        registering a key within a specific network subnet.
3220
3221        Args:
3222            netuid: The network UID for which to query the minimum stake.
3223
3224        Returns:
3225            The minimum stake required for key registration in nanos.
3226
3227        Raises:
3228            QueryError: If the query to the network fails or is invalid.
3229        """
3230
3231        return self.query("MinStake", params=[netuid])
3232
3233    def get_stakefrom(
3234        self,
3235        key: Ss58Address,
3236    ) -> dict[str, int]:
3237        """
3238        Retrieves the stake amounts from all stakers to a specific staked address.
3239
3240        Queries the network for the stakes received by a particular staked address
3241        from all stakers.
3242
3243        Args:
3244            key: The address of the key receiving the stakes.
3245
3246        Returns:
3247            A dictionary mapping staker addresses to their respective stake amounts.
3248
3249        Raises:
3250            QueryError: If the query to the network fails or is invalid.
3251        """
3252
3253        # Has to use query map in order to iterate through the storage prefix.
3254        return self.query_map("StakeFrom", [key], extract_value=False).get(
3255            "StakeFrom", {}
3256        )
3257
3258    def get_staketo(
3259        self,
3260        key: Ss58Address,
3261    ) -> dict[str, int]:
3262        """
3263        Retrieves the stake amounts provided by a specific staker to all staked addresses.
3264
3265        Queries the network for the stakes provided by a particular staker to
3266        all staked addresses.
3267
3268        Args:
3269            key: The address of the key providing the stakes.
3270
3271        Returns:
3272            A dictionary mapping staked addresses to their respective received stake amounts.
3273
3274        Raises:
3275            QueryError: If the query to the network fails or is invalid.
3276        """
3277
3278        # Has to use query map in order to iterate through the storage prefix.
3279        return self.query_map("StakeTo", [key], extract_value=False).get(
3280            "StakeTo", {}
3281        )
3282
3283    def get_balance(
3284        self,
3285        addr: Ss58Address,
3286    ) -> int:
3287        """
3288        Retrieves the balance of a specific key.
3289
3290        Args:
3291            addr: The address of the key to query the balance for.
3292
3293        Returns:
3294            The balance of the specified key.
3295
3296        Raises:
3297            QueryError: If the query to the network fails or is invalid.
3298        """
3299
3300        result = self.query("Account", module="System", params=[addr])
3301
3302        return result["data"]["free"]
3303
3304    def get_block(self, block_hash: str | None = None) -> dict[Any, Any] | None:
3305        """
3306        Retrieves information about a specific block in the network.
3307
3308        Queries the network for details about a block, such as its number,
3309        hash, and other relevant information.
3310
3311        Returns:
3312            The requested information about the block,
3313            or None if the block does not exist
3314            or the information is not available.
3315
3316        Raises:
3317            QueryError: If the query to the network fails or is invalid.
3318        """
3319
3320        with self.get_conn() as substrate:
3321            block: dict[Any, Any] | None = substrate.get_block(  # type: ignore
3322                block_hash  # type: ignore
3323            )
3324
3325        return block
3326
3327    def get_existential_deposit(self, block_hash: str | None = None) -> int:
3328        """
3329        Retrieves the existential deposit value for the network.
3330
3331        The existential deposit is the minimum balance that must be maintained
3332        in an account to prevent it from being purged. Denotated in nano units.
3333
3334        Returns:
3335            The existential deposit value in nano units.
3336        Note:
3337            The value returned is a fixed value defined in the
3338            client and may not reflect changes in the network's configuration.
3339        """
3340
3341        with self.get_conn() as substrate:
3342            result: int = substrate.get_constant(  #  type: ignore
3343                "Balances", "ExistentialDeposit", block_hash
3344            ).value  #  type: ignore
3345
3346        return result
3347
3348    def get_voting_power_delegators(self) -> list[Ss58Address]:
3349        result = self.query(
3350            "NotDelegatingVotingPower", [], module="GovernanceModule"
3351        )
3352        return result
3353
3354    def add_transfer_dao_treasury_proposal(
3355        self,
3356        key: Keypair,
3357        data: str,
3358        amount_nano: int,
3359        dest: Ss58Address,
3360    ):
3361        params = {"dest": dest, "value": amount_nano, "data": data}
3362
3363        return self.compose_call(
3364            module="GovernanceModule",
3365            fn="add_transfer_dao_treasury_proposal",
3366            params=params,
3367            key=key,
3368        )

A client for interacting with Commune network nodes, querying storage, submitting transactions, etc.

Attributes:
  • wait_for_finalization: Whether to wait for transaction finalization.

Example:

client = CommuneClient()
client.query(name='function_name', params=['param1', 'param2'])
Raises:
  • AssertionError: If the maximum connections value is less than or equal to zero.
CommuneClient( url: str, num_connections: int = 1, wait_for_finalization: bool = False, timeout: int | None = None)
103    def __init__(
104        self,
105        url: str,
106        num_connections: int = 1,
107        wait_for_finalization: bool = False,
108        timeout: int | None = None,
109    ):
110        """
111        Args:
112            url: The URL of the network node to connect to.
113            num_connections: The number of websocket connections to be opened.
114        """
115        assert num_connections > 0
116        self._num_connections = num_connections
117        self.wait_for_finalization = wait_for_finalization
118        self._connection_queue = queue.Queue(num_connections)
119        self.url = url
120
121        for _ in range(num_connections):
122            ws_options: dict[str, int] = {}
123            if timeout is not None:
124                ws_options["timeout"] = timeout
125            self._ws_options = ws_options
126            self._connection_queue.put(
127                _instantiate_substrateinterface(
128                    url, ws_options, threading.Lock()
129                )
130            )
Arguments:
  • url: The URL of the network node to connect to.
  • num_connections: The number of websocket connections to be opened.
wait_for_finalization: bool
url: str
connections: int
132    @property
133    def connections(self) -> int:
134        """
135        Gets the maximum allowed number of simultaneous connections to the
136        network node.
137        """
138        return self._num_connections

Gets the maximum allowed number of simultaneous connections to the network node.

@contextmanager
def get_conn(self, timeout: float | None = None, init: bool = False):
140    @contextmanager
141    def get_conn(self, timeout: float | None = None, init: bool = False):
142        """
143        Context manager to get a connection from the pool.
144
145        Tries to get a connection from the pool queue. If the queue is empty,
146        it blocks for `timeout` seconds until a connection is available. If
147        `timeout` is None, it blocks indefinitely.
148
149        Args:
150            timeout: The maximum time in seconds to wait for a connection.
151
152        Yields:
153            The connection object from the pool.
154
155        Raises:
156            QueueEmptyError: If no connection is available within the timeout
157              period.
158        """
159        conn = self._connection_queue.get(timeout=timeout)
160        if init:
161            conn.substrate.init_runtime()  # type: ignore
162        try:
163            gc.collect()
164            if conn.substrate.websocket and conn.substrate.websocket.connected:  # type: ignore
165                with conn.lock:
166                    yield conn.substrate
167            else:
168                # reconnects
169                conn.stop_event.set()
170                conn = _instantiate_substrateinterface(
171                    self.url, self._ws_options, threading.Lock()
172                )
173                with conn.lock:
174                    yield conn.substrate
175        finally:
176            self._connection_queue.put(conn)

Context manager to get a connection from the pool.

Tries to get a connection from the pool queue. If the queue is empty, it blocks for timeout seconds until a connection is available. If timeout is None, it blocks indefinitely.

Arguments:
  • timeout: The maximum time in seconds to wait for a connection.
Yields:

The connection object from the pool.

Raises:
  • QueueEmptyError: If no connection is available within the timeout period.
def query_batch( self, functions: dict[str, list[tuple[str, list[typing.Any]]]]) -> dict[str, str]:
651    def query_batch(
652        self, functions: dict[str, list[tuple[str, list[Any]]]]
653    ) -> dict[str, str]:
654        """
655        Executes batch queries on a substrate and returns results in a dictionary format.
656
657        Args:
658            substrate: An instance of SubstrateInterface to interact with the substrate.
659            functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls (function name and parameters).
660
661        Returns:
662            A dictionary where keys are storage function names and values are the query results.
663
664        Raises:
665            Exception: If no result is found from the batch queries.
666
667        Example:
668            >>> query_batch(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
669            {'function_name': 'query_result', ...}
670        """
671
672        result: dict[str, str] = {}
673        if not functions:
674            raise Exception("No result")
675        with self.get_conn(init=True) as substrate:
676            for module, queries in functions.items():
677                storage_keys: list[Any] = []
678                for fn, params in queries:
679                    storage_function = substrate.create_storage_key(  # type: ignore
680                        pallet=module, storage_function=fn, params=params
681                    )
682                    storage_keys.append(storage_function)
683
684                block_hash = substrate.get_block_hash()
685                responses: list[Any] = substrate.query_multi(  # type: ignore
686                    storage_keys=storage_keys, block_hash=block_hash
687                )
688
689                for item in responses:
690                    fun = item[0]
691                    query = item[1]
692                    storage_fun = fun.storage_function
693                    result[storage_fun] = query.value
694
695        return result

Executes batch queries on a substrate and returns results in a dictionary format.

Arguments:
  • substrate: An instance of SubstrateInterface to interact with the substrate.
  • functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls (function name and parameters).
Returns:

A dictionary where keys are storage function names and values are the query results.

Raises:
  • Exception: If no result is found from the batch queries.
Example:
>>> query_batch(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
{'function_name': 'query_result', ...}
def query_batch_map( self, functions: dict[str, list[tuple[str, list[typing.Any]]]], block_hash: str | None = None) -> dict[str, dict[typing.Any, typing.Any]]:
697    def query_batch_map(
698        self,
699        functions: dict[str, list[tuple[str, list[Any]]]],
700        block_hash: str | None = None,
701    ) -> dict[str, dict[Any, Any]]:
702        """
703        Queries multiple storage functions using a map batch approach and returns the combined result.
704
705        Args:
706            substrate: An instance of SubstrateInterface for substrate interaction.
707            functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls.
708
709        Returns:
710            The combined result of the map batch query.
711
712        Example:
713            >>> query_batch_map(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
714            # Returns the combined result of the map batch query
715        """
716        multi_result: dict[str, dict[Any, Any]] = {}
717
718        def recursive_update(
719            d: dict[str, dict[T1, T2] | dict[str, Any]],
720            u: Mapping[str, dict[Any, Any] | str],
721        ) -> dict[str, dict[T1, T2]]:
722            for k, v in u.items():
723                if isinstance(v, dict):
724                    d[k] = recursive_update(d.get(k, {}), v)  # type: ignore
725                else:
726                    d[k] = v  # type: ignore
727            return d  # type: ignore
728
729        def get_page():
730            send, prefix_list = self._get_storage_keys(
731                storage, queries, block_hash
732            )
733            with self.get_conn(init=True) as substrate:
734                function_parameters = self._get_lists(
735                    storage, queries, substrate
736                )
737            responses = self._rpc_request_batch(send)
738            # assumption because send is just the storage_function keys
739            # so it should always be really small regardless of the amount of queries
740            assert len(responses) == 1
741            res = responses[0]
742            built_payload: list[tuple[str, list[Any]]] = []
743            for result_keys in res:
744                built_payload.append(
745                    ("state_queryStorageAt", [result_keys, block_hash])
746                )
747            _, chunks_info = self._make_request_smaller(
748                built_payload, prefix_list, function_parameters
749            )
750            chunks_response, chunks_info = self._rpc_request_batch_chunked(
751                chunks_info
752            )
753            return chunks_response, chunks_info
754
755        if not block_hash:
756            with self.get_conn(init=True) as substrate:
757                block_hash = substrate.get_block_hash()
758        for storage, queries in functions.items():
759            chunks, chunks_info = get_page()
760            # if this doesn't happen something is wrong on the code
761            # and we won't be able to decode the data properly
762            assert len(chunks) == len(chunks_info)
763            for chunk_info, response in zip(chunks_info, chunks):
764                storage_result = self._decode_response(
765                    response,
766                    chunk_info.fun_params,
767                    chunk_info.prefix_list,
768                    block_hash,
769                )
770                multi_result = recursive_update(multi_result, storage_result)
771
772        return multi_result

Queries multiple storage functions using a map batch approach and returns the combined result.

Arguments:
  • substrate: An instance of SubstrateInterface for substrate interaction.
  • functions (dict[str, list[query_call]]): A dictionary mapping module names to lists of query calls.
Returns:

The combined result of the map batch query.

Example:
>>> query_batch_map(substrate_instance, {'module_name': [('function_name', ['param1', 'param2'])]})
<h1 id="returns-the-combined-result-of-the-map-batch-query">Returns the combined result of the map batch query</h1>
def query( self, name: str, params: list[typing.Any] = [], module: str = 'SubspaceModule', block_hash: str | None = None) -> Any:
774    def query(
775        self,
776        name: str,
777        params: list[Any] = [],
778        module: str = "SubspaceModule",
779        block_hash: str | None = None,
780    ) -> Any:
781        """
782        Queries a storage function on the network.
783
784        Sends a query to the network and retrieves data from a
785        specified storage function.
786
787        Args:
788            name: The name of the storage function to query.
789            params: The parameters to pass to the storage function.
790            module: The module where the storage function is located.
791
792        Returns:
793            The result of the query from the network.
794
795        Raises:
796            NetworkQueryError: If the query fails or is invalid.
797        """
798
799        result = self.query_batch({module: [(name, params)]})
800
801        return result[name]

Queries a storage function on the network.

Sends a query to the network and retrieves data from a specified storage function.

Arguments:
  • name: The name of the storage function to query.
  • params: The parameters to pass to the storage function.
  • module: The module where the storage function is located.
Returns:

The result of the query from the network.

Raises:
  • NetworkQueryError: If the query fails or is invalid.
def query_map( self, name: str, params: list[typing.Any] = [], module: str = 'SubspaceModule', extract_value: bool = True, block_hash: str | None = None) -> dict[typing.Any, typing.Any]:
803    def query_map(
804        self,
805        name: str,
806        params: list[Any] = [],
807        module: str = "SubspaceModule",
808        extract_value: bool = True,
809        block_hash: str | None = None,
810    ) -> dict[Any, Any]:
811        """
812        Queries a storage map from a network node.
813
814        Args:
815            name: The name of the storage map to query.
816            params: A list of parameters for the query.
817            module: The module in which the storage map is located.
818
819        Returns:
820            A dictionary representing the key-value pairs
821              retrieved from the storage map.
822
823        Raises:
824            QueryError: If the query to the network fails or is invalid.
825        """
826
827        result = self.query_batch_map({module: [(name, params)]}, block_hash)
828
829        if extract_value:
830            return {k.value: v.value for k, v in result}  # type: ignore
831
832        return result

Queries a storage map from a network node.

Arguments:
  • name: The name of the storage map to query.
  • params: A list of parameters for the query.
  • module: The module in which the storage map is located.
Returns:

A dictionary representing the key-value pairs retrieved from the storage map.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def compose_call( self, fn: str, params: dict[str, typing.Any], key: substrateinterface.keypair.Keypair | None, module: str = 'SubspaceModule', wait_for_inclusion: bool = True, wait_for_finalization: bool | None = None, sudo: bool = False, unsigned: bool = False) -> substrateinterface.base.ExtrinsicReceipt:
834    def compose_call(
835        self,
836        fn: str,
837        params: dict[str, Any],
838        key: Keypair | None,
839        module: str = "SubspaceModule",
840        wait_for_inclusion: bool = True,
841        wait_for_finalization: bool | None = None,
842        sudo: bool = False,
843        unsigned: bool = False,
844    ) -> ExtrinsicReceipt:
845        """
846        Composes and submits a call to the network node.
847
848        Composes and signs a call with the provided keypair, and submits it to
849        the network. The call can be a standard extrinsic or a sudo extrinsic if
850        elevated permissions are required. The method can optionally wait for
851        the call's inclusion in a block and/or its finalization.
852
853        Args:
854            fn: The function name to call on the network.
855            params: A dictionary of parameters for the call.
856            key: The keypair for signing the extrinsic.
857            module: The module containing the function.
858            wait_for_inclusion: Wait for the call's inclusion in a block.
859            wait_for_finalization: Wait for the transaction's finalization.
860            sudo: Execute the call as a sudo (superuser) operation.
861
862        Returns:
863            The receipt of the submitted extrinsic, if
864              `wait_for_inclusion` is True. Otherwise, returns a string
865              identifier of the extrinsic.
866
867        Raises:
868            ChainTransactionError: If the transaction fails.
869        """
870
871        if key is None and not unsigned:
872            raise ValueError("Key must be provided for signed extrinsics.")
873
874        with self.get_conn() as substrate:
875            if wait_for_finalization is None:
876                wait_for_finalization = self.wait_for_finalization
877
878            call = substrate.compose_call(  # type: ignore
879                call_module=module, call_function=fn, call_params=params
880            )
881            if sudo:
882                call = substrate.compose_call(  # type: ignore
883                    call_module="Sudo",
884                    call_function="sudo",
885                    call_params={
886                        "call": call.value,  # type: ignore
887                    },
888                )
889
890            if not unsigned:
891                assert key is not None
892                extrinsic = substrate.create_signed_extrinsic(  # type: ignore
893                    call=call,
894                    keypair=key,
895                )
896            else:
897                extrinsic = substrate.create_unsigned_extrinsic(call=call)  # type: ignore
898
899            response = substrate.submit_extrinsic(
900                extrinsic=extrinsic,
901                wait_for_inclusion=wait_for_inclusion,
902                wait_for_finalization=wait_for_finalization,
903            )
904        if wait_for_inclusion:
905            if not response.is_success:
906                raise ChainTransactionError(
907                    response.error_message,  # type: ignore
908                    response,  # type: ignore
909                )
910
911        return response

Composes and submits a call to the network node.

Composes and signs a call with the provided keypair, and submits it to the network. The call can be a standard extrinsic or a sudo extrinsic if elevated permissions are required. The method can optionally wait for the call's inclusion in a block and/or its finalization.

Arguments:
  • fn: The function name to call on the network.
  • params: A dictionary of parameters for the call.
  • key: The keypair for signing the extrinsic.
  • module: The module containing the function.
  • wait_for_inclusion: Wait for the call's inclusion in a block.
  • wait_for_finalization: Wait for the transaction's finalization.
  • sudo: Execute the call as a sudo (superuser) operation.
Returns:

The receipt of the submitted extrinsic, if wait_for_inclusion is True. Otherwise, returns a string identifier of the extrinsic.

Raises:
  • ChainTransactionError: If the transaction fails.
def compose_call_multisig( self, fn: str, params: dict[str, typing.Any], key: substrateinterface.keypair.Keypair, signatories: list[communex.types.Ss58Address], threshold: int, module: str = 'SubspaceModule', wait_for_inclusion: bool = True, wait_for_finalization: bool | None = None, sudo: bool = False, era: dict[str, int] | None = None) -> substrateinterface.base.ExtrinsicReceipt:
 913    def compose_call_multisig(
 914        self,
 915        fn: str,
 916        params: dict[str, Any],
 917        key: Keypair,
 918        signatories: list[Ss58Address],
 919        threshold: int,
 920        module: str = "SubspaceModule",
 921        wait_for_inclusion: bool = True,
 922        wait_for_finalization: bool | None = None,
 923        sudo: bool = False,
 924        era: dict[str, int] | None = None,
 925    ) -> ExtrinsicReceipt:
 926        """
 927        Composes and submits a multisignature call to the network node.
 928
 929        This method allows the composition and submission of a call that
 930        requires multiple signatures for execution, known as a multisignature
 931        call. It supports specifying signatories, a threshold of signatures for
 932        the call's execution, and an optional era for the call's mortality. The
 933        call can be a standard extrinsic, a sudo extrinsic for elevated
 934        permissions, or a multisig extrinsic if multiple signatures are
 935        required. Optionally, the method can wait for the call's inclusion in a
 936        block and/or its finalization. Make sure to pass all keys,
 937        that are part of the multisignature.
 938
 939        Args:
 940            fn: The function name to call on the network. params: A dictionary
 941            of parameters for the call. key: The keypair for signing the
 942            extrinsic. signatories: List of SS58 addresses of the signatories.
 943            Include ALL KEYS that are part of the multisig. threshold: The
 944            minimum number of signatories required to execute the extrinsic.
 945            module: The module containing the function to call.
 946            wait_for_inclusion: Whether to wait for the call's inclusion in a
 947            block. wait_for_finalization: Whether to wait for the transaction's
 948            finalization. sudo: Execute the call as a sudo (superuser)
 949            operation. era: Specifies the call's mortality in terms of blocks in
 950            the format
 951                {'period': amount_blocks}. If omitted, the extrinsic is
 952                immortal.
 953
 954        Returns:
 955            The receipt of the submitted extrinsic if `wait_for_inclusion` is
 956            True. Otherwise, returns a string identifier of the extrinsic.
 957
 958        Raises:
 959            ChainTransactionError: If the transaction fails.
 960        """
 961
 962        # getting the call ready
 963        with self.get_conn() as substrate:
 964            if wait_for_finalization is None:
 965                wait_for_finalization = self.wait_for_finalization
 966
 967            # prepares the `GenericCall` object
 968            call = substrate.compose_call(  # type: ignore
 969                call_module=module, call_function=fn, call_params=params
 970            )
 971            if sudo:
 972                call = substrate.compose_call(  # type: ignore
 973                    call_module="Sudo",
 974                    call_function="sudo",
 975                    call_params={
 976                        "call": call.value,  # type: ignore
 977                    },
 978                )
 979
 980            # modify the rpc methods at runtime, to allow for correct payment
 981            # fee calculation parity has a bug in this version,
 982            # where the method has to be removed
 983            rpc_methods = substrate.config.get("rpc_methods")  # type: ignore
 984
 985            if "state_call" in rpc_methods:  # type: ignore
 986                rpc_methods.remove("state_call")  # type: ignore
 987
 988            # create the multisig account
 989            multisig_acc = substrate.generate_multisig_account(  # type: ignore
 990                signatories, threshold
 991            )
 992
 993            # send the multisig extrinsic
 994            extrinsic = substrate.create_multisig_extrinsic(  # type: ignore
 995                call=call,  # type: ignore
 996                keypair=key,
 997                multisig_account=multisig_acc,  # type: ignore
 998                era=era,  # type: ignore
 999            )  # type: ignore
1000
1001            response = substrate.submit_extrinsic(
1002                extrinsic=extrinsic,
1003                wait_for_inclusion=wait_for_inclusion,
1004                wait_for_finalization=wait_for_finalization,
1005            )
1006
1007        if wait_for_inclusion:
1008            if not response.is_success:
1009                raise ChainTransactionError(
1010                    response.error_message,  # type: ignore
1011                    response,  # type: ignore
1012                )
1013
1014        return response

Composes and submits a multisignature call to the network node.

This method allows the composition and submission of a call that requires multiple signatures for execution, known as a multisignature call. It supports specifying signatories, a threshold of signatures for the call's execution, and an optional era for the call's mortality. The call can be a standard extrinsic, a sudo extrinsic for elevated permissions, or a multisig extrinsic if multiple signatures are required. Optionally, the method can wait for the call's inclusion in a block and/or its finalization. Make sure to pass all keys, that are part of the multisignature.

Arguments:
  • fn: The function name to call on the network. params: A dictionary
  • of parameters for the call. key: The keypair for signing the
  • extrinsic. signatories: List of SS58 addresses of the signatories.
  • Include ALL KEYS that are part of the multisig. threshold: The
  • minimum number of signatories required to execute the extrinsic.
  • module: The module containing the function to call.
  • wait_for_inclusion: Whether to wait for the call's inclusion in a
  • block. wait_for_finalization: Whether to wait for the transaction's
  • finalization. sudo: Execute the call as a sudo (superuser)
  • operation. era: Specifies the call's mortality in terms of blocks in
  • the format {'period': amount_blocks}. If omitted, the extrinsic is immortal.
Returns:

The receipt of the submitted extrinsic if wait_for_inclusion is True. Otherwise, returns a string identifier of the extrinsic.

Raises:
  • ChainTransactionError: If the transaction fails.
def transfer( self, key: substrateinterface.keypair.Keypair, amount: int, dest: communex.types.Ss58Address) -> substrateinterface.base.ExtrinsicReceipt:
1016    def transfer(
1017        self,
1018        key: Keypair,
1019        amount: int,
1020        dest: Ss58Address,
1021    ) -> ExtrinsicReceipt:
1022        """
1023        Transfers a specified amount of tokens from the signer's account to the
1024        specified account.
1025
1026        Args:
1027            key: The keypair associated with the sender's account.
1028            amount: The amount to transfer, in nanotokens.
1029            dest: The SS58 address of the recipient.
1030
1031        Returns:
1032            A receipt of the transaction.
1033
1034        Raises:
1035            InsufficientBalanceError: If the sender's account does not have
1036              enough balance.
1037            ChainTransactionError: If the transaction fails.
1038        """
1039
1040        params = {"dest": dest, "value": amount}
1041
1042        return self.compose_call(
1043            module="Balances", fn="transfer_keep_alive", params=params, key=key
1044        )

Transfers a specified amount of tokens from the signer's account to the specified account.

Arguments:
  • key: The keypair associated with the sender's account.
  • amount: The amount to transfer, in nanotokens.
  • dest: The SS58 address of the recipient.
Returns:

A receipt of the transaction.

Raises:
  • InsufficientBalanceError: If the sender's account does not have enough balance.
  • ChainTransactionError: If the transaction fails.
def transfer_multiple( self, key: substrateinterface.keypair.Keypair, destinations: list[communex.types.Ss58Address], amounts: list[int], netuid: str | int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1046    def transfer_multiple(
1047        self,
1048        key: Keypair,
1049        destinations: list[Ss58Address],
1050        amounts: list[int],
1051        netuid: str | int = 0,
1052    ) -> ExtrinsicReceipt:
1053        """
1054        Transfers specified amounts of tokens from the signer's account to
1055        multiple target accounts.
1056
1057        The `destinations` and `amounts` lists must be of the same length.
1058
1059        Args:
1060            key: The keypair associated with the sender's account.
1061            destinations: A list of SS58 addresses of the recipients.
1062            amounts: Amount to transfer to each recipient, in nanotokens.
1063            netuid: The network identifier.
1064
1065        Returns:
1066            A receipt of the transaction.
1067
1068        Raises:
1069            InsufficientBalanceError: If the sender's account does not have
1070              enough balance for all transfers.
1071            ChainTransactionError: If the transaction fails.
1072        """
1073
1074        assert len(destinations) == len(amounts)
1075
1076        # extract existential deposit from amounts
1077        existential_deposit = self.get_existential_deposit()
1078        amounts = [a - existential_deposit for a in amounts]
1079
1080        params = {
1081            "netuid": netuid,
1082            "destinations": destinations,
1083            "amounts": amounts,
1084        }
1085
1086        return self.compose_call(
1087            module="SubspaceModule",
1088            fn="transfer_multiple",
1089            params=params,
1090            key=key,
1091        )

Transfers specified amounts of tokens from the signer's account to multiple target accounts.

The destinations and amounts lists must be of the same length.

Arguments:
  • key: The keypair associated with the sender's account.
  • destinations: A list of SS58 addresses of the recipients.
  • amounts: Amount to transfer to each recipient, in nanotokens.
  • netuid: The network identifier.
Returns:

A receipt of the transaction.

Raises:
  • InsufficientBalanceError: If the sender's account does not have enough balance for all transfers.
  • ChainTransactionError: If the transaction fails.
def stake( self, key: substrateinterface.keypair.Keypair, amount: int, dest: communex.types.Ss58Address) -> substrateinterface.base.ExtrinsicReceipt:
1093    def stake(
1094        self,
1095        key: Keypair,
1096        amount: int,
1097        dest: Ss58Address,
1098    ) -> ExtrinsicReceipt:
1099        """
1100        Stakes the specified amount of tokens to a module key address.
1101
1102        Args:
1103            key: The keypair associated with the staker's account.
1104            amount: The amount of tokens to stake, in nanotokens.
1105            dest: The SS58 address of the module key to stake to.
1106            netuid: The network identifier.
1107
1108        Returns:
1109            A receipt of the staking transaction.
1110
1111        Raises:
1112            InsufficientBalanceError: If the staker's account does not have
1113              enough balance.
1114            ChainTransactionError: If the transaction fails.
1115        """
1116
1117        params = {"amount": amount, "module_key": dest}
1118
1119        return self.compose_call(fn="add_stake", params=params, key=key)

Stakes the specified amount of tokens to a module key address.

Arguments:
  • key: The keypair associated with the staker's account.
  • amount: The amount of tokens to stake, in nanotokens.
  • dest: The SS58 address of the module key to stake to.
  • netuid: The network identifier.
Returns:

A receipt of the staking transaction.

Raises:
  • InsufficientBalanceError: If the staker's account does not have enough balance.
  • ChainTransactionError: If the transaction fails.
def unstake( self, key: substrateinterface.keypair.Keypair, amount: int, dest: communex.types.Ss58Address) -> substrateinterface.base.ExtrinsicReceipt:
1121    def unstake(
1122        self,
1123        key: Keypair,
1124        amount: int,
1125        dest: Ss58Address,
1126    ) -> ExtrinsicReceipt:
1127        """
1128        Unstakes the specified amount of tokens from a module key address.
1129
1130        Args:
1131            key: The keypair associated with the unstaker's account.
1132            amount: The amount of tokens to unstake, in nanotokens.
1133            dest: The SS58 address of the module key to unstake from.
1134            netuid: The network identifier.
1135
1136        Returns:
1137            A receipt of the unstaking transaction.
1138
1139        Raises:
1140            InsufficientStakeError: If the staked key does not have enough
1141              staked tokens by the signer key.
1142            ChainTransactionError: If the transaction fails.
1143        """
1144
1145        params = {"amount": amount, "module_key": dest}
1146        return self.compose_call(fn="remove_stake", params=params, key=key)

Unstakes the specified amount of tokens from a module key address.

Arguments:
  • key: The keypair associated with the unstaker's account.
  • amount: The amount of tokens to unstake, in nanotokens.
  • dest: The SS58 address of the module key to unstake from.
  • netuid: The network identifier.
Returns:

A receipt of the unstaking transaction.

Raises:
  • InsufficientStakeError: If the staked key does not have enough staked tokens by the signer key.
  • ChainTransactionError: If the transaction fails.
def update_module( self, key: substrateinterface.keypair.Keypair, name: str, address: str, metadata: str | None = None, delegation_fee: int = 20, weight_setting_delegation_fee: int | None = None, netuid: int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1148    def update_module(
1149        self,
1150        key: Keypair,
1151        name: str,
1152        address: str,
1153        metadata: str | None = None,
1154        delegation_fee: int = 20,
1155        weight_setting_delegation_fee: int | None = None,
1156        netuid: int = 0,
1157    ) -> ExtrinsicReceipt:
1158        """
1159        Updates the parameters of a registered module.
1160
1161        The delegation fee must be an integer between 0 and 100.
1162
1163        Args:
1164            key: The keypair associated with the module's account.
1165            name: The new name for the module. If None, the name is not updated.
1166            address: The new address for the module.
1167                If None, the address is not updated.
1168            delegation_fee: The new delegation fee for the module,
1169                between 0 and 100.
1170            netuid: The network identifier.
1171
1172        Returns:
1173            A receipt of the module update transaction.
1174
1175        Raises:
1176            InvalidParameterError: If the provided parameters are invalid.
1177            ChainTransactionError: If the transaction fails.
1178        """
1179
1180        assert isinstance(delegation_fee, int)
1181        params = {
1182            "netuid": netuid,
1183            "name": name,
1184            "address": address,
1185            "stake_delegation_fee": delegation_fee,
1186            "validator_weight_fee": weight_setting_delegation_fee,
1187            "metadata": metadata,
1188        }
1189
1190        response = self.compose_call("update_module", params=params, key=key)
1191
1192        return response

Updates the parameters of a registered module.

The delegation fee must be an integer between 0 and 100.

Arguments:
  • key: The keypair associated with the module's account.
  • name: The new name for the module. If None, the name is not updated.
  • address: The new address for the module. If None, the address is not updated.
  • delegation_fee: The new delegation fee for the module, between 0 and 100.
  • netuid: The network identifier.
Returns:

A receipt of the module update transaction.

Raises:
  • InvalidParameterError: If the provided parameters are invalid.
  • ChainTransactionError: If the transaction fails.
def register_module( self, key: substrateinterface.keypair.Keypair, name: str, address: str | None = None, subnet: str = 'Rootnet', metadata: str | None = None) -> substrateinterface.base.ExtrinsicReceipt:
1194    def register_module(
1195        self,
1196        key: Keypair,
1197        name: str,
1198        address: str | None = None,
1199        subnet: str = "Rootnet",
1200        metadata: str | None = None,
1201    ) -> ExtrinsicReceipt:
1202        """
1203        Registers a new module in the network.
1204
1205        Args:
1206            key: The keypair used for registering the module.
1207            name: The name of the module. If None, a default or previously
1208                set name is used. # How does this work?
1209            address: The address of the module. If None, a default or
1210                previously set address is used. # How does this work?
1211            subnet: The network subnet to register the module in.
1212            min_stake: The minimum stake required for the module, in nanotokens.
1213                If None, a default value is used.
1214
1215        Returns:
1216            A receipt of the registration transaction.
1217
1218        Raises:
1219            InvalidParameterError: If the provided parameters are invalid.
1220            ChainTransactionError: If the transaction fails.
1221        """
1222
1223        key_addr = key.ss58_address
1224
1225        params = {
1226            "network_name": subnet,
1227            "address": address,
1228            "name": name,
1229            "module_key": key_addr,
1230            "metadata": metadata,
1231        }
1232
1233        response = self.compose_call("register", params=params, key=key)
1234        return response

Registers a new module in the network.

Arguments:
  • key: The keypair used for registering the module.
  • name: The name of the module. If None, a default or previously set name is used. # How does this work?
  • address: The address of the module. If None, a default or previously set address is used. # How does this work?
  • subnet: The network subnet to register the module in.
  • min_stake: The minimum stake required for the module, in nanotokens. If None, a default value is used.
Returns:

A receipt of the registration transaction.

Raises:
  • InvalidParameterError: If the provided parameters are invalid.
  • ChainTransactionError: If the transaction fails.
def deregister_module( self, key: substrateinterface.keypair.Keypair, netuid: int) -> substrateinterface.base.ExtrinsicReceipt:
1236    def deregister_module(self, key: Keypair, netuid: int) -> ExtrinsicReceipt:
1237        """
1238        Deregisters a module from the network.
1239
1240        Args:
1241            key: The keypair associated with the module's account.
1242            netuid: The network identifier.
1243
1244        Returns:
1245            A receipt of the module deregistration transaction.
1246
1247        Raises:
1248            ChainTransactionError: If the transaction fails.
1249        """
1250
1251        params = {"netuid": netuid}
1252
1253        response = self.compose_call("deregister", params=params, key=key)
1254
1255        return response

Deregisters a module from the network.

Arguments:
  • key: The keypair associated with the module's account.
  • netuid: The network identifier.
Returns:

A receipt of the module deregistration transaction.

Raises:
  • ChainTransactionError: If the transaction fails.
def register_subnet( self, key: substrateinterface.keypair.Keypair, name: str, metadata: str | None = None) -> substrateinterface.base.ExtrinsicReceipt:
1257    def register_subnet(
1258        self, key: Keypair, name: str, metadata: str | None = None
1259    ) -> ExtrinsicReceipt:
1260        """
1261        Registers a new subnet in the network.
1262
1263        Args:
1264            key (Keypair): The keypair used for registering the subnet.
1265            name (str): The name of the subnet to be registered.
1266            metadata (str | None, optional): Additional metadata for the subnet. Defaults to None.
1267
1268        Returns:
1269            ExtrinsicReceipt: A receipt of the subnet registration transaction.
1270
1271        Raises:
1272            ChainTransactionError: If the transaction fails.
1273        """
1274
1275        params = {
1276            "name": name,
1277            "metadata": metadata,
1278        }
1279
1280        response = self.compose_call("register_subnet", params=params, key=key)
1281
1282        return response

Registers a new subnet in the network.

Arguments:
  • key (Keypair): The keypair used for registering the subnet.
  • name (str): The name of the subnet to be registered.
  • metadata (str | None, optional): Additional metadata for the subnet. Defaults to None.
Returns:

ExtrinsicReceipt: A receipt of the subnet registration transaction.

Raises:
  • ChainTransactionError: If the transaction fails.
def vote( self, key: substrateinterface.keypair.Keypair, uids: list[int], weights: list[int], netuid: int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1284    def vote(
1285        self,
1286        key: Keypair,
1287        uids: list[int],
1288        weights: list[int],
1289        netuid: int = 0,
1290    ) -> ExtrinsicReceipt:
1291        """
1292        Casts votes on a list of module UIDs with corresponding weights.
1293
1294        The length of the UIDs list and the weights list should be the same.
1295        Each weight corresponds to the UID at the same index.
1296
1297        Args:
1298            key: The keypair used for signing the vote transaction.
1299            uids: A list of module UIDs to vote on.
1300            weights: A list of weights corresponding to each UID.
1301            netuid: The network identifier.
1302
1303        Returns:
1304            A receipt of the voting transaction.
1305
1306        Raises:
1307            InvalidParameterError: If the lengths of UIDs and weights lists
1308                do not match.
1309            ChainTransactionError: If the transaction fails.
1310        """
1311
1312        assert len(uids) == len(weights)
1313
1314        params = {
1315            "uids": uids,
1316            "weights": weights,
1317            "netuid": netuid,
1318        }
1319
1320        response = self.compose_call(
1321            "set_weights",
1322            params=params,
1323            key=key,
1324            module="SubnetEmissionModule",
1325        )
1326
1327        return response

Casts votes on a list of module UIDs with corresponding weights.

The length of the UIDs list and the weights list should be the same. Each weight corresponds to the UID at the same index.

Arguments:
  • key: The keypair used for signing the vote transaction.
  • uids: A list of module UIDs to vote on.
  • weights: A list of weights corresponding to each UID.
  • netuid: The network identifier.
Returns:

A receipt of the voting transaction.

Raises:
  • InvalidParameterError: If the lengths of UIDs and weights lists do not match.
  • ChainTransactionError: If the transaction fails.
def delegate_weight_control( self, key: substrateinterface.keypair.Keypair, target: communex.types.Ss58Address, netuid: int):
1329    def delegate_weight_control(
1330        self, key: Keypair, target: Ss58Address, netuid: int
1331    ):
1332        """
1333        delegates weight setting control from the current account to the target account.
1334        Both accounts have to be registered and have a valid validator `spec`
1335        """
1336
1337        params = {
1338            "netuid": netuid,
1339            "target": target,
1340        }
1341
1342        response = self.compose_call(
1343            "delegate_weight_control",
1344            params=params,
1345            key=key,
1346            module="SubnetEmissionModule",
1347        )
1348        return response

delegates weight setting control from the current account to the target account. Both accounts have to be registered and have a valid validator spec

def add_authorities( self, key: substrateinterface.keypair.Keypair, new_authorities: list[tuple[communex.types.Ss58Address, tuple[str, str]]]):
1350    def add_authorities(
1351        self,
1352        key: Keypair,
1353        new_authorities: list[tuple[Ss58Address, tuple[str, str]]],
1354    ):
1355        """
1356        only for the sudo key
1357        give the authorities as hexadecimal
1358        """
1359        params = {
1360            "new_authorities": new_authorities,
1361        }
1362        response = self.compose_call(
1363            "add_authorities",
1364            params=params,
1365            key=key,
1366            module="Offworker",
1367            sudo=True,
1368        )
1369        return response

only for the sudo key give the authorities as hexadecimal

def vote_encrypted( self, key: substrateinterface.keypair.Keypair, uids: list[int], weights: list[int], netuid: int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1371    def vote_encrypted(
1372        self,
1373        key: Keypair,
1374        uids: list[int],
1375        weights: list[int],
1376        netuid: int = 0,
1377    ) -> ExtrinsicReceipt:
1378        """
1379        Casts encrypted votes for module UIDs with corresponding weights.
1380
1381        Args:
1382            key (Keypair): The keypair used for signing the transaction.
1383            uids (list[int]): List of UIDs to vote for.
1384            weights (list[int]): List of weights corresponding to each UID.
1385            netuid (int, optional): Network UID. Defaults to 0.
1386
1387        Returns:
1388            ExtrinsicReceipt: Receipt of the submitted extrinsic.
1389
1390        Raises:
1391            ValueError: If there's a length mismatch between UIDs and weights,
1392                if subnet data is not found, or if subnet key format is invalid.
1393        """
1394        if len(uids) != len(weights):
1395            raise ValueError("Length mismatch between UIDs and weights")
1396
1397        subnet_data = self.query(
1398            "SubnetDecryptionData",
1399            module="SubnetEmissionModule",
1400            params=[netuid],
1401        )
1402        if not subnet_data:
1403            raise ValueError("Subnet data not found")
1404
1405        subnet_key = subnet_data.get("node_public_key")
1406        if not subnet_key or len(subnet_key) < 2:
1407            raise ValueError("Invalid subnet key format")
1408
1409        vote_data = list(zip(uids, weights))
1410        decryptor = (
1411            bytes_from_hex(subnet_key[0]),
1412            bytes_from_hex(subnet_key[1]),
1413        )
1414        validator_key = [int(x) for x in key.public_key]
1415
1416        encrypted_weights = encrypt_weights(decryptor, vote_data, validator_key)
1417        weights_hash = hashlib.sha256(str(weights).encode()).hexdigest()
1418
1419        params = {
1420            "uids": uids,
1421            "encrypted_weights": encrypted_weights,
1422            "netuid": netuid,
1423            "decrypted_weights_hash": weights_hash,
1424        }
1425
1426        return self.compose_call(
1427            "set_weights_encrypted",
1428            params=params,
1429            key=key,
1430            module="SubnetEmissionModule",
1431        )

Casts encrypted votes for module UIDs with corresponding weights.

Arguments:
  • key (Keypair): The keypair used for signing the transaction.
  • uids (list[int]): List of UIDs to vote for.
  • weights (list[int]): List of weights corresponding to each UID.
  • netuid (int, optional): Network UID. Defaults to 0.
Returns:

ExtrinsicReceipt: Receipt of the submitted extrinsic.

Raises:
  • ValueError: If there's a length mismatch between UIDs and weights, if subnet data is not found, or if subnet key format is invalid.
def update_subnet( self, key: substrateinterface.keypair.Keypair, params: communex.types.SubnetParams, netuid: int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1433    def update_subnet(
1434        self,
1435        key: Keypair,
1436        params: SubnetParams,
1437        netuid: int = 0,
1438    ) -> ExtrinsicReceipt:
1439        """
1440        Update a subnet's configuration.
1441
1442        It requires the founder key for authorization.
1443
1444        Args:
1445            key: The founder keypair of the subnet.
1446            params: The new parameters for the subnet.
1447            netuid: The network identifier.
1448
1449        Returns:
1450            A receipt of the subnet update transaction.
1451
1452        Raises:
1453            AuthorizationError: If the key is not authorized.
1454            ChainTransactionError: If the transaction fails.
1455        """
1456
1457        general_params = dict(params)
1458        general_params["netuid"] = netuid
1459        if general_params.get("subnet_metadata") is None:
1460            general_params["metadata"] = None
1461        else:
1462            general_params["metadata"] = general_params["subnet_metadata"]
1463
1464        response = self.compose_call(
1465            fn="update_subnet",
1466            params=general_params,
1467            key=key,
1468        )
1469
1470        return response

Update a subnet's configuration.

It requires the founder key for authorization.

Arguments:
  • key: The founder keypair of the subnet.
  • params: The new parameters for the subnet.
  • netuid: The network identifier.
Returns:

A receipt of the subnet update transaction.

Raises:
  • AuthorizationError: If the key is not authorized.
  • ChainTransactionError: If the transaction fails.
def transfer_stake( self, key: substrateinterface.keypair.Keypair, amount: int, from_module_key: communex.types.Ss58Address, dest_module_address: communex.types.Ss58Address) -> substrateinterface.base.ExtrinsicReceipt:
1472    def transfer_stake(
1473        self,
1474        key: Keypair,
1475        amount: int,
1476        from_module_key: Ss58Address,
1477        dest_module_address: Ss58Address,
1478    ) -> ExtrinsicReceipt:
1479        """
1480        Realocate staked tokens from one staked module to another module.
1481
1482        Args:
1483            key: The keypair associated with the account that is delegating the tokens.
1484            amount: The amount of staked tokens to transfer, in nanotokens.
1485            from_module_key: The SS58 address of the module you want to transfer from (currently delegated by the key).
1486            dest_module_address: The SS58 address of the destination (newly delegated key).
1487            netuid: The network identifier.
1488
1489        Returns:
1490            A receipt of the stake transfer transaction.
1491
1492        Raises:
1493            InsufficientStakeError: If the source module key does not have
1494            enough staked tokens. ChainTransactionError: If the transaction
1495            fails.
1496        """
1497
1498        amount = amount - self.get_existential_deposit()
1499
1500        params = {
1501            "amount": amount,
1502            "module_key": from_module_key,
1503            "new_module_key": dest_module_address,
1504        }
1505
1506        response = self.compose_call("transfer_stake", key=key, params=params)
1507
1508        return response

Realocate staked tokens from one staked module to another module.

Arguments:
  • key: The keypair associated with the account that is delegating the tokens.
  • amount: The amount of staked tokens to transfer, in nanotokens.
  • from_module_key: The SS58 address of the module you want to transfer from (currently delegated by the key).
  • dest_module_address: The SS58 address of the destination (newly delegated key).
  • netuid: The network identifier.
Returns:

A receipt of the stake transfer transaction.

Raises:
  • InsufficientStakeError: If the source module key does not have
  • enough staked tokens. ChainTransactionError: If the transaction
  • fails.
def bridge(self, key: substrateinterface.keypair.Keypair, amount: int):
1510    def bridge(
1511        self,
1512        key: Keypair,
1513        amount: int,
1514    ):
1515        """
1516        Bridge tokens from the Subspace network to the Torus network.
1517
1518        Args:
1519            key: The keypair associated with the account that is bridging the tokens.
1520            amount: The amount of tokens to bridge, in nanotokens.
1521
1522        Returns:
1523            A receipt of the bridging transaction.
1524
1525        Raises:
1526            InsufficientBalanceError: If the account does not have enough balance.
1527            ChainTransactionError: If the transaction fails.
1528        """
1529
1530        params = {"amount": amount}
1531
1532        response = self.compose_call("bridge", key=key, params=params)
1533
1534        return response

Bridge tokens from the Subspace network to the Torus network.

Arguments:
  • key: The keypair associated with the account that is bridging the tokens.
  • amount: The amount of tokens to bridge, in nanotokens.
Returns:

A receipt of the bridging transaction.

Raises:
  • InsufficientBalanceError: If the account does not have enough balance.
  • ChainTransactionError: If the transaction fails.
def bridge_withdraw(self, key: substrateinterface.keypair.Keypair, amount: int):
1536    def bridge_withdraw(
1537        self,
1538        key: Keypair,
1539        amount: int,
1540    ):
1541        """
1542        Withdraw bridged tokens from the Torus network to the Subspace network.
1543
1544        Args:
1545            key: The keypair associated with the account that is withdrawing the tokens.
1546            amount: The amount of tokens to withdraw, in nanotokens.
1547
1548        Returns:
1549            A receipt of the withdrawal transaction.
1550
1551        Raises:
1552            InsufficientBalanceError: If the account does not have enough balance.
1553            ChainTransactionError: If the transaction fails.
1554        """
1555
1556        params = {"amount": amount}
1557
1558        response = self.compose_call("bridge_withdraw", key=key, params=params)
1559
1560        return response

Withdraw bridged tokens from the Torus network to the Subspace network.

Arguments:
  • key: The keypair associated with the account that is withdrawing the tokens.
  • amount: The amount of tokens to withdraw, in nanotokens.
Returns:

A receipt of the withdrawal transaction.

Raises:
  • InsufficientBalanceError: If the account does not have enough balance.
  • ChainTransactionError: If the transaction fails.
def multiunstake( self, key: substrateinterface.keypair.Keypair, keys: list[communex.types.Ss58Address], amounts: list[int]) -> substrateinterface.base.ExtrinsicReceipt:
1562    def multiunstake(
1563        self,
1564        key: Keypair,
1565        keys: list[Ss58Address],
1566        amounts: list[int],
1567    ) -> ExtrinsicReceipt:
1568        """
1569        Unstakes tokens from multiple module keys.
1570
1571        And the lists `keys` and `amounts` must be of the same length. Each
1572        amount corresponds to the module key at the same index.
1573
1574        Args:
1575            key: The keypair associated with the unstaker's account.
1576            keys: A list of SS58 addresses of the module keys to unstake from.
1577            amounts: A list of amounts to unstake from each module key,
1578              in nanotokens.
1579            netuid: The network identifier.
1580
1581        Returns:
1582            A receipt of the multi-unstaking transaction.
1583
1584        Raises:
1585            MismatchedLengthError: If the lengths of keys and amounts lists do
1586            not match. InsufficientStakeError: If any of the module keys do not
1587            have enough staked tokens. ChainTransactionError: If the transaction
1588            fails.
1589        """
1590
1591        assert len(keys) == len(amounts)
1592
1593        params = {"module_keys": keys, "amounts": amounts}
1594
1595        response = self.compose_call(
1596            "remove_stake_multiple", params=params, key=key
1597        )
1598
1599        return response

Unstakes tokens from multiple module keys.

And the lists keys and amounts must be of the same length. Each amount corresponds to the module key at the same index.

Arguments:
  • key: The keypair associated with the unstaker's account.
  • keys: A list of SS58 addresses of the module keys to unstake from.
  • amounts: A list of amounts to unstake from each module key, in nanotokens.
  • netuid: The network identifier.
Returns:

A receipt of the multi-unstaking transaction.

Raises:
  • MismatchedLengthError: If the lengths of keys and amounts lists do
  • not match. InsufficientStakeError: If any of the module keys do not
  • have enough staked tokens. ChainTransactionError: If the transaction
  • fails.
def multistake( self, key: substrateinterface.keypair.Keypair, keys: list[communex.types.Ss58Address], amounts: list[int]) -> substrateinterface.base.ExtrinsicReceipt:
1601    def multistake(
1602        self,
1603        key: Keypair,
1604        keys: list[Ss58Address],
1605        amounts: list[int],
1606    ) -> ExtrinsicReceipt:
1607        """
1608        Stakes tokens to multiple module keys.
1609
1610        The lengths of the `keys` and `amounts` lists must be the same. Each
1611        amount corresponds to the module key at the same index.
1612
1613        Args:
1614            key: The keypair associated with the staker's account.
1615            keys: A list of SS58 addresses of the module keys to stake to.
1616            amounts: A list of amounts to stake to each module key,
1617                in nanotokens.
1618            netuid: The network identifier.
1619
1620        Returns:
1621            A receipt of the multi-staking transaction.
1622
1623        Raises:
1624            MismatchedLengthError: If the lengths of keys and amounts lists
1625                do not match.
1626            ChainTransactionError: If the transaction fails.
1627        """
1628
1629        assert len(keys) == len(amounts)
1630
1631        params = {
1632            "module_keys": keys,
1633            "amounts": amounts,
1634        }
1635
1636        response = self.compose_call(
1637            "add_stake_multiple", params=params, key=key
1638        )
1639
1640        return response

Stakes tokens to multiple module keys.

The lengths of the keys and amounts lists must be the same. Each amount corresponds to the module key at the same index.

Arguments:
  • key: The keypair associated with the staker's account.
  • keys: A list of SS58 addresses of the module keys to stake to.
  • amounts: A list of amounts to stake to each module key, in nanotokens.
  • netuid: The network identifier.
Returns:

A receipt of the multi-staking transaction.

Raises:
  • MismatchedLengthError: If the lengths of keys and amounts lists do not match.
  • ChainTransactionError: If the transaction fails.
def add_profit_shares( self, key: substrateinterface.keypair.Keypair, keys: list[communex.types.Ss58Address], shares: list[int]) -> substrateinterface.base.ExtrinsicReceipt:
1642    def add_profit_shares(
1643        self,
1644        key: Keypair,
1645        keys: list[Ss58Address],
1646        shares: list[int],
1647    ) -> ExtrinsicReceipt:
1648        """
1649        Allocates profit shares to multiple keys.
1650
1651        The lists `keys` and `shares` must be of the same length,
1652        with each share amount corresponding to the key at the same index.
1653
1654        Args:
1655            key: The keypair associated with the account
1656                distributing the shares.
1657            keys: A list of SS58 addresses to allocate shares to.
1658            shares: A list of share amounts to allocate to each key,
1659                in nanotokens.
1660
1661        Returns:
1662            A receipt of the profit sharing transaction.
1663
1664        Raises:
1665            MismatchedLengthError: If the lengths of keys and shares
1666                lists do not match.
1667            ChainTransactionError: If the transaction fails.
1668        """
1669
1670        assert len(keys) == len(shares)
1671
1672        params = {"keys": keys, "shares": shares}
1673
1674        response = self.compose_call(
1675            "add_profit_shares", params=params, key=key
1676        )
1677
1678        return response

Allocates profit shares to multiple keys.

The lists keys and shares must be of the same length, with each share amount corresponding to the key at the same index.

Arguments:
  • key: The keypair associated with the account distributing the shares.
  • keys: A list of SS58 addresses to allocate shares to.
  • shares: A list of share amounts to allocate to each key, in nanotokens.
Returns:

A receipt of the profit sharing transaction.

Raises:
  • MismatchedLengthError: If the lengths of keys and shares lists do not match.
  • ChainTransactionError: If the transaction fails.
def add_subnet_proposal( self, key: substrateinterface.keypair.Keypair, params: dict[str, typing.Any], ipfs: str, netuid: int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1680    def add_subnet_proposal(
1681        self, key: Keypair, params: dict[str, Any], ipfs: str, netuid: int = 0
1682    ) -> ExtrinsicReceipt:
1683        """
1684        Submits a proposal for creating or modifying a subnet within the
1685        network.
1686
1687        The proposal includes various parameters like the name, founder, share
1688        allocations, and other subnet-specific settings.
1689
1690        Args:
1691            key: The keypair used for signing the proposal transaction.
1692            params: The parameters for the subnet proposal.
1693            netuid: The network identifier.
1694
1695        Returns:
1696            A receipt of the subnet proposal transaction.
1697
1698        Raises:
1699            InvalidParameterError: If the provided subnet
1700                parameters are invalid.
1701            ChainTransactionError: If the transaction fails.
1702        """
1703
1704        general_params = dict(params)
1705        general_params["netuid"] = netuid
1706        general_params["data"] = ipfs
1707        if "metadata" not in general_params:
1708            general_params["metadata"] = None
1709
1710        # general_params["burn_config"] = json.dumps(general_params["burn_config"])
1711        response = self.compose_call(
1712            fn="add_subnet_params_proposal",
1713            params=general_params,
1714            key=key,
1715            module="GovernanceModule",
1716        )
1717
1718        return response

Submits a proposal for creating or modifying a subnet within the network.

The proposal includes various parameters like the name, founder, share allocations, and other subnet-specific settings.

Arguments:
  • key: The keypair used for signing the proposal transaction.
  • params: The parameters for the subnet proposal.
  • netuid: The network identifier.
Returns:

A receipt of the subnet proposal transaction.

Raises:
  • InvalidParameterError: If the provided subnet parameters are invalid.
  • ChainTransactionError: If the transaction fails.
def add_custom_proposal( self, key: substrateinterface.keypair.Keypair, cid: str) -> substrateinterface.base.ExtrinsicReceipt:
1720    def add_custom_proposal(
1721        self,
1722        key: Keypair,
1723        cid: str,
1724    ) -> ExtrinsicReceipt:
1725        params = {"data": cid}
1726
1727        response = self.compose_call(
1728            fn="add_global_custom_proposal",
1729            params=params,
1730            key=key,
1731            module="GovernanceModule",
1732        )
1733        return response
def add_custom_subnet_proposal( self, key: substrateinterface.keypair.Keypair, cid: str, netuid: int = 0) -> substrateinterface.base.ExtrinsicReceipt:
1735    def add_custom_subnet_proposal(
1736        self,
1737        key: Keypair,
1738        cid: str,
1739        netuid: int = 0,
1740    ) -> ExtrinsicReceipt:
1741        """
1742        Submits a proposal for creating or modifying a custom subnet within the
1743        network.
1744
1745        The proposal includes various parameters like the name, founder, share
1746        allocations, and other subnet-specific settings.
1747
1748        Args:
1749            key: The keypair used for signing the proposal transaction.
1750            params: The parameters for the subnet proposal.
1751            netuid: The network identifier.
1752
1753        Returns:
1754            A receipt of the subnet proposal transaction.
1755        """
1756
1757        params = {
1758            "data": cid,
1759            "netuid": netuid,
1760        }
1761
1762        response = self.compose_call(
1763            fn="add_subnet_custom_proposal",
1764            params=params,
1765            key=key,
1766            module="GovernanceModule",
1767        )
1768
1769        return response

Submits a proposal for creating or modifying a custom subnet within the network.

The proposal includes various parameters like the name, founder, share allocations, and other subnet-specific settings.

Arguments:
  • key: The keypair used for signing the proposal transaction.
  • params: The parameters for the subnet proposal.
  • netuid: The network identifier.
Returns:

A receipt of the subnet proposal transaction.

def add_global_proposal( self, key: substrateinterface.keypair.Keypair, params: communex.types.NetworkParams, cid: str | None) -> substrateinterface.base.ExtrinsicReceipt:
1771    def add_global_proposal(
1772        self,
1773        key: Keypair,
1774        params: NetworkParams,
1775        cid: str | None,
1776    ) -> ExtrinsicReceipt:
1777        """
1778        Submits a proposal for altering the global network parameters.
1779
1780        Allows for the submission of a proposal to
1781        change various global parameters
1782        of the network, such as emission rates, rate limits, and voting
1783        thresholds. It is used to
1784        suggest changes that affect the entire network's operation.
1785
1786        Args:
1787            key: The keypair used for signing the proposal transaction.
1788            params: A dictionary containing global network parameters
1789                    like maximum allowed subnets, modules,
1790                    transaction rate limits, and others.
1791
1792        Returns:
1793            A receipt of the global proposal transaction.
1794
1795        Raises:
1796            InvalidParameterError: If the provided network
1797                parameters are invalid.
1798            ChainTransactionError: If the transaction fails.
1799        """
1800        general_params = cast(dict[str, Any], params)
1801        cid = cid or ""
1802        general_params["data"] = cid
1803
1804        response = self.compose_call(
1805            fn="add_global_params_proposal",
1806            params=general_params,
1807            key=key,
1808            module="GovernanceModule",
1809        )
1810
1811        return response

Submits a proposal for altering the global network parameters.

Allows for the submission of a proposal to change various global parameters of the network, such as emission rates, rate limits, and voting thresholds. It is used to suggest changes that affect the entire network's operation.

Arguments:
  • key: The keypair used for signing the proposal transaction.
  • params: A dictionary containing global network parameters like maximum allowed subnets, modules, transaction rate limits, and others.
Returns:

A receipt of the global proposal transaction.

Raises:
  • InvalidParameterError: If the provided network parameters are invalid.
  • ChainTransactionError: If the transaction fails.
def vote_on_proposal( self, key: substrateinterface.keypair.Keypair, proposal_id: int, agree: bool) -> substrateinterface.base.ExtrinsicReceipt:
1813    def vote_on_proposal(
1814        self,
1815        key: Keypair,
1816        proposal_id: int,
1817        agree: bool,
1818    ) -> ExtrinsicReceipt:
1819        """
1820        Casts a vote on a specified proposal within the network.
1821
1822        Args:
1823            key: The keypair used for signing the vote transaction.
1824            proposal_id: The unique identifier of the proposal to vote on.
1825
1826        Returns:
1827            A receipt of the voting transaction in nanotokens.
1828
1829        Raises:
1830            InvalidProposalIDError: If the provided proposal ID does not
1831                exist or is invalid.
1832            ChainTransactionError: If the transaction fails.
1833        """
1834
1835        params = {"proposal_id": proposal_id, "agree": agree}
1836
1837        response = self.compose_call(
1838            "vote_proposal",
1839            key=key,
1840            params=params,
1841            module="GovernanceModule",
1842        )
1843
1844        return response

Casts a vote on a specified proposal within the network.

Arguments:
  • key: The keypair used for signing the vote transaction.
  • proposal_id: The unique identifier of the proposal to vote on.
Returns:

A receipt of the voting transaction in nanotokens.

Raises:
  • InvalidProposalIDError: If the provided proposal ID does not exist or is invalid.
  • ChainTransactionError: If the transaction fails.
def unvote_on_proposal( self, key: substrateinterface.keypair.Keypair, proposal_id: int) -> substrateinterface.base.ExtrinsicReceipt:
1846    def unvote_on_proposal(
1847        self,
1848        key: Keypair,
1849        proposal_id: int,
1850    ) -> ExtrinsicReceipt:
1851        """
1852        Retracts a previously cast vote on a specified proposal.
1853
1854        Args:
1855            key: The keypair used for signing the unvote transaction.
1856            proposal_id: The unique identifier of the proposal to withdraw the
1857                vote from.
1858
1859        Returns:
1860            A receipt of the unvoting transaction in nanotokens.
1861
1862        Raises:
1863            InvalidProposalIDError: If the provided proposal ID does not
1864                exist or is invalid.
1865            ChainTransactionError: If the transaction fails to be processed, or
1866                if there was no prior vote to retract.
1867        """
1868
1869        params = {"proposal_id": proposal_id}
1870
1871        response = self.compose_call(
1872            "remove_vote_proposal",
1873            key=key,
1874            params=params,
1875            module="GovernanceModule",
1876        )
1877
1878        return response

Retracts a previously cast vote on a specified proposal.

Arguments:
  • key: The keypair used for signing the unvote transaction.
  • proposal_id: The unique identifier of the proposal to withdraw the vote from.
Returns:

A receipt of the unvoting transaction in nanotokens.

Raises:
  • InvalidProposalIDError: If the provided proposal ID does not exist or is invalid.
  • ChainTransactionError: If the transaction fails to be processed, or if there was no prior vote to retract.
def enable_vote_power_delegation( self, key: substrateinterface.keypair.Keypair) -> substrateinterface.base.ExtrinsicReceipt:
1880    def enable_vote_power_delegation(self, key: Keypair) -> ExtrinsicReceipt:
1881        """
1882        Enables vote power delegation for the signer's account.
1883
1884        Args:
1885            key: The keypair used for signing the delegation transaction.
1886
1887        Returns:
1888            A receipt of the vote power delegation transaction.
1889
1890        Raises:
1891            ChainTransactionError: If the transaction fails.
1892        """
1893
1894        response = self.compose_call(
1895            "enable_vote_power_delegation",
1896            params={},
1897            key=key,
1898            module="GovernanceModule",
1899        )
1900
1901        return response

Enables vote power delegation for the signer's account.

Arguments:
  • key: The keypair used for signing the delegation transaction.
Returns:

A receipt of the vote power delegation transaction.

Raises:
  • ChainTransactionError: If the transaction fails.
def disable_vote_power_delegation( self, key: substrateinterface.keypair.Keypair) -> substrateinterface.base.ExtrinsicReceipt:
1903    def disable_vote_power_delegation(self, key: Keypair) -> ExtrinsicReceipt:
1904        """
1905        Disables vote power delegation for the signer's account.
1906
1907        Args:
1908            key: The keypair used for signing the delegation transaction.
1909
1910        Returns:
1911            A receipt of the vote power delegation transaction.
1912
1913        Raises:
1914            ChainTransactionError: If the transaction fails.
1915        """
1916
1917        response = self.compose_call(
1918            "disable_vote_power_delegation",
1919            params={},
1920            key=key,
1921            module="GovernanceModule",
1922        )
1923
1924        return response

Disables vote power delegation for the signer's account.

Arguments:
  • key: The keypair used for signing the delegation transaction.
Returns:

A receipt of the vote power delegation transaction.

Raises:
  • ChainTransactionError: If the transaction fails.
def add_dao_application( self, key: substrateinterface.keypair.Keypair, application_key: communex.types.Ss58Address, data: str) -> substrateinterface.base.ExtrinsicReceipt:
1926    def add_dao_application(
1927        self, key: Keypair, application_key: Ss58Address, data: str
1928    ) -> ExtrinsicReceipt:
1929        """
1930        Submits a new application to the general subnet DAO.
1931
1932        Args:
1933            key: The keypair used for signing the application transaction.
1934            application_key: The SS58 address of the application key.
1935            data: The data associated with the application.
1936
1937        Returns:
1938            A receipt of the application transaction.
1939
1940        Raises:
1941            ChainTransactionError: If the transaction fails.
1942        """
1943
1944        params = {"application_key": application_key, "data": data}
1945
1946        response = self.compose_call(
1947            "add_dao_application",
1948            module="GovernanceModule",
1949            key=key,
1950            params=params,
1951        )
1952
1953        return response

Submits a new application to the general subnet DAO.

Arguments:
  • key: The keypair used for signing the application transaction.
  • application_key: The SS58 address of the application key.
  • data: The data associated with the application.
Returns:

A receipt of the application transaction.

Raises:
  • ChainTransactionError: If the transaction fails.
def query_map_curator_applications(self) -> dict[str, dict[str, str]]:
1955    def query_map_curator_applications(self) -> dict[str, dict[str, str]]:
1956        query_result = self.query_map(
1957            "CuratorApplications",
1958            module="GovernanceModule",
1959            params=[],
1960            extract_value=False,
1961        )
1962        applications = query_result.get("CuratorApplications", {})
1963        return applications
def query_map_proposals(self, extract_value: bool = False) -> dict[int, dict[str, typing.Any]]:
1965    def query_map_proposals(
1966        self, extract_value: bool = False
1967    ) -> dict[int, dict[str, Any]]:
1968        """
1969        Retrieves a mappping of proposals from the network.
1970
1971        Queries the network and returns a mapping of proposal IDs to
1972        their respective parameters.
1973
1974        Returns:
1975            A dictionary mapping proposal IDs
1976            to dictionaries of their parameters.
1977
1978        Raises:
1979            QueryError: If the query to the network fails or is invalid.
1980        """
1981
1982        return self.query_map(
1983            "Proposals", extract_value=extract_value, module="GovernanceModule"
1984        )["Proposals"]

Retrieves a mappping of proposals from the network.

Queries the network and returns a mapping of proposal IDs to their respective parameters.

Returns:

A dictionary mapping proposal IDs to dictionaries of their parameters.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_weights( self, netuid: int = 0, extract_value: bool = False) -> dict[int, list[tuple[int, int]]] | None:
1986    def query_map_weights(
1987        self, netuid: int = 0, extract_value: bool = False
1988    ) -> dict[int, list[tuple[int, int]]] | None:
1989        """
1990        Retrieves a mapping of weights for keys on the network.
1991
1992        Queries the network and returns a mapping of key UIDs to
1993        their respective weights.
1994
1995        Args:
1996            netuid: The network UID from which to get the weights.
1997
1998        Returns:
1999            A dictionary mapping key UIDs to lists of their weights.
2000
2001        Raises:
2002            QueryError: If the query to the network fails or is invalid.
2003        """
2004
2005        weights_dict = self.query_map(
2006            "Weights",
2007            [netuid],
2008            extract_value=extract_value,
2009            module="SubnetEmissionModule",
2010        ).get("Weights")
2011        return weights_dict

Retrieves a mapping of weights for keys on the network.

Queries the network and returns a mapping of key UIDs to their respective weights.

Arguments:
  • netuid: The network UID from which to get the weights.
Returns:

A dictionary mapping key UIDs to lists of their weights.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_key( self, netuid: int = 0, extract_value: bool = False) -> dict[int, communex.types.Ss58Address]:
2013    def query_map_key(
2014        self,
2015        netuid: int = 0,
2016        extract_value: bool = False,
2017    ) -> dict[int, Ss58Address]:
2018        """
2019        Retrieves a map of keys from the network.
2020
2021        Fetches a mapping of key UIDs to their associated
2022        addresses on the network.
2023        The query can be targeted at a specific network UID if required.
2024
2025        Args:
2026            netuid: The network UID from which to get the keys.
2027
2028        Returns:
2029            A dictionary mapping key UIDs to their addresses.
2030
2031        Raises:
2032            QueryError: If the query to the network fails or is invalid.
2033        """
2034        return self.query_map("Keys", [netuid], extract_value=extract_value)[
2035            "Keys"
2036        ]

Retrieves a map of keys from the network.

Fetches a mapping of key UIDs to their associated addresses on the network. The query can be targeted at a specific network UID if required.

Arguments:
  • netuid: The network UID from which to get the keys.
Returns:

A dictionary mapping key UIDs to their addresses.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_address(self, netuid: int = 0, extract_value: bool = False) -> dict[int, str]:
2038    def query_map_address(
2039        self, netuid: int = 0, extract_value: bool = False
2040    ) -> dict[int, str]:
2041        """
2042        Retrieves a map of key addresses from the network.
2043
2044        Queries the network for a mapping of key UIDs to their addresses.
2045
2046        Args:
2047            netuid: The network UID from which to get the addresses.
2048
2049        Returns:
2050            A dictionary mapping key UIDs to their addresses.
2051
2052        Raises:
2053            QueryError: If the query to the network fails or is invalid.
2054        """
2055
2056        return self.query_map("Address", [netuid], extract_value=extract_value)[
2057            "Address"
2058        ]

Retrieves a map of key addresses from the network.

Queries the network for a mapping of key UIDs to their addresses.

Arguments:
  • netuid: The network UID from which to get the addresses.
Returns:

A dictionary mapping key UIDs to their addresses.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_emission(self, extract_value: bool = False) -> dict[int, list[int]]:
2060    def query_map_emission(
2061        self, extract_value: bool = False
2062    ) -> dict[int, list[int]]:
2063        """
2064        Retrieves a map of emissions for keys on the network.
2065
2066        Queries the network to get a mapping of
2067        key UIDs to their emission values.
2068
2069        Returns:
2070            A dictionary mapping key UIDs to lists of their emission values.
2071
2072        Raises:
2073            QueryError: If the query to the network fails or is invalid.
2074        """
2075
2076        return self.query_map("Emission", extract_value=extract_value)[
2077            "Emission"
2078        ]

Retrieves a map of emissions for keys on the network.

Queries the network to get a mapping of key UIDs to their emission values.

Returns:

A dictionary mapping key UIDs to lists of their emission values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_pending_emission(self, extract_value: bool = False) -> int:
2080    def query_map_pending_emission(self, extract_value: bool = False) -> int:
2081        """
2082        Retrieves a map of pending emissions for the subnets.
2083
2084        Queries the network for a mapping of subnet UIDs to their pending emission values.
2085
2086        Returns:
2087            A dictionary mapping subnet UIDs to their pending emission values.
2088
2089        Raises:
2090            QueryError: If the query to the network fails or is invalid.
2091        """
2092        return self.query_map(
2093            "PendingEmission",
2094            extract_value=extract_value,
2095            module="SubnetEmissionModule",
2096        )["PendingEmission"]

Retrieves a map of pending emissions for the subnets.

Queries the network for a mapping of subnet UIDs to their pending emission values.

Returns:

A dictionary mapping subnet UIDs to their pending emission values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_subnet_emission(self, extract_value: bool = False) -> dict[int, int]:
2098    def query_map_subnet_emission(
2099        self, extract_value: bool = False
2100    ) -> dict[int, int]:
2101        """
2102        Retrieves a map of subnet emissions for the network.
2103
2104        Queries the network for a mapping of subnet UIDs to their emission values.
2105
2106        Returns:
2107            A dictionary mapping subnet UIDs to their emission values.
2108
2109        Raises:
2110            QueryError: If the query to the network fails or is invalid.
2111        """
2112
2113        return self.query_map(
2114            "SubnetEmission",
2115            extract_value=extract_value,
2116            module="SubnetEmissionModule",
2117        )["SubnetEmission"]

Retrieves a map of subnet emissions for the network.

Queries the network for a mapping of subnet UIDs to their emission values.

Returns:

A dictionary mapping subnet UIDs to their emission values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_subnet_consensus(self, extract_value: bool = False) -> dict[int, str]:
2119    def query_map_subnet_consensus(
2120        self, extract_value: bool = False
2121    ) -> dict[int, str]:
2122        """
2123        Retrieves a map of subnet consensus types for the network.
2124
2125        Queries the network for a mapping of subnet UIDs to their consensus types.
2126
2127        Returns:
2128            A dictionary mapping subnet UIDs to their consensus types.
2129
2130        Raises:
2131            QueryError: If the query to the network fails or is invalid.
2132        """
2133
2134        return self.query_map(
2135            "SubnetConsensusType",
2136            extract_value=extract_value,
2137            module="SubnetEmissionModule",
2138        )["SubnetConsensusType"]

Retrieves a map of subnet consensus types for the network.

Queries the network for a mapping of subnet UIDs to their consensus types.

Returns:

A dictionary mapping subnet UIDs to their consensus types.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_incentive(self, extract_value: bool = False) -> dict[int, list[int]]:
2140    def query_map_incentive(
2141        self, extract_value: bool = False
2142    ) -> dict[int, list[int]]:
2143        """
2144        Retrieves a mapping of incentives for keys on the network.
2145
2146        Queries the network and returns a mapping of key UIDs to
2147        their respective incentive values.
2148
2149        Returns:
2150            A dictionary mapping key UIDs to lists of their incentive values.
2151
2152        Raises:
2153            QueryError: If the query to the network fails or is invalid.
2154        """
2155
2156        return self.query_map("Incentive", extract_value=extract_value)[
2157            "Incentive"
2158        ]

Retrieves a mapping of incentives for keys on the network.

Queries the network and returns a mapping of key UIDs to their respective incentive values.

Returns:

A dictionary mapping key UIDs to lists of their incentive values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_dividend(self, extract_value: bool = False) -> dict[int, list[int]]:
2160    def query_map_dividend(
2161        self, extract_value: bool = False
2162    ) -> dict[int, list[int]]:
2163        """
2164        Retrieves a mapping of dividends for keys on the network.
2165
2166        Queries the network for a mapping of key UIDs to
2167        their dividend values.
2168
2169        Returns:
2170            A dictionary mapping key UIDs to lists of their dividend values.
2171
2172        Raises:
2173            QueryError: If the query to the network fails or is invalid.
2174        """
2175
2176        return self.query_map("Dividends", extract_value=extract_value)[
2177            "Dividends"
2178        ]

Retrieves a mapping of dividends for keys on the network.

Queries the network for a mapping of key UIDs to their dividend values.

Returns:

A dictionary mapping key UIDs to lists of their dividend values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_regblock(self, netuid: int = 0, extract_value: bool = False) -> dict[int, int]:
2180    def query_map_regblock(
2181        self, netuid: int = 0, extract_value: bool = False
2182    ) -> dict[int, int]:
2183        """
2184        Retrieves a mapping of registration blocks for keys on the network.
2185
2186        Queries the network for a mapping of key UIDs to
2187        the blocks where they were registered.
2188
2189        Args:
2190            netuid: The network UID from which to get the registration blocks.
2191
2192        Returns:
2193            A dictionary mapping key UIDs to their registration blocks.
2194
2195        Raises:
2196            QueryError: If the query to the network fails or is invalid.
2197        """
2198
2199        return self.query_map(
2200            "RegistrationBlock", [netuid], extract_value=extract_value
2201        )["RegistrationBlock"]

Retrieves a mapping of registration blocks for keys on the network.

Queries the network for a mapping of key UIDs to the blocks where they were registered.

Arguments:
  • netuid: The network UID from which to get the registration blocks.
Returns:

A dictionary mapping key UIDs to their registration blocks.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_lastupdate(self, extract_value: bool = False) -> dict[int, list[int]]:
2203    def query_map_lastupdate(
2204        self, extract_value: bool = False
2205    ) -> dict[int, list[int]]:
2206        """
2207        Retrieves a mapping of the last update times for keys on the network.
2208
2209        Queries the network for a mapping of key UIDs to their last update times.
2210
2211        Returns:
2212            A dictionary mapping key UIDs to lists of their last update times.
2213
2214        Raises:
2215            QueryError: If the query to the network fails or is invalid.
2216        """
2217
2218        return self.query_map("LastUpdate", extract_value=extract_value)[
2219            "LastUpdate"
2220        ]

Retrieves a mapping of the last update times for keys on the network.

Queries the network for a mapping of key UIDs to their last update times.

Returns:

A dictionary mapping key UIDs to lists of their last update times.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_stakefrom( self, extract_value: bool = False) -> dict[communex.types.Ss58Address, list[tuple[communex.types.Ss58Address, int]]]:
2222    def query_map_stakefrom(
2223        self, extract_value: bool = False
2224    ) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
2225        """
2226        Retrieves a mapping of stakes from various sources for keys on the network.
2227
2228        Queries the network to obtain a mapping of key addresses to the sources
2229        and amounts of stakes they have received.
2230
2231        Args:
2232            netuid: The network UID from which to get the stakes.
2233
2234        Returns:
2235            A dictionary mapping key addresses to lists of tuples
2236            (module_key_address, amount).
2237
2238        Raises:
2239            QueryError: If the query to the network fails or is invalid.
2240        """
2241
2242        result = self.query_map("StakeFrom", [], extract_value=extract_value)[
2243            "StakeFrom"
2244        ]
2245
2246        return transform_stake_dmap(result)

Retrieves a mapping of stakes from various sources for keys on the network.

Queries the network to obtain a mapping of key addresses to the sources and amounts of stakes they have received.

Arguments:
  • netuid: The network UID from which to get the stakes.
Returns:

A dictionary mapping key addresses to lists of tuples (module_key_address, amount).

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_staketo( self, extract_value: bool = False) -> dict[communex.types.Ss58Address, list[tuple[communex.types.Ss58Address, int]]]:
2248    def query_map_staketo(
2249        self, extract_value: bool = False
2250    ) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
2251        """
2252        Retrieves a mapping of stakes to destinations for keys on the network.
2253
2254        Queries the network for a mapping of key addresses to the destinations
2255        and amounts of stakes they have made.
2256
2257        Args:
2258            netuid: The network UID from which to get the stakes.
2259
2260        Returns:
2261            A dictionary mapping key addresses to lists of tuples
2262            (module_key_address, amount).
2263
2264        Raises:
2265            QueryError: If the query to the network fails or is invalid.
2266        """
2267
2268        result = self.query_map("StakeTo", [], extract_value=extract_value)[
2269            "StakeTo"
2270        ]
2271        return transform_stake_dmap(result)

Retrieves a mapping of stakes to destinations for keys on the network.

Queries the network for a mapping of key addresses to the destinations and amounts of stakes they have made.

Arguments:
  • netuid: The network UID from which to get the stakes.
Returns:

A dictionary mapping key addresses to lists of tuples (module_key_address, amount).

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_delegationfee(self, netuid: int = 0, extract_value: bool = False) -> dict[str, int]:
2273    def query_map_delegationfee(
2274        self, netuid: int = 0, extract_value: bool = False
2275    ) -> dict[str, int]:
2276        """
2277        Retrieves a mapping of delegation fees for keys on the network.
2278
2279        Queries the network to obtain a mapping of key addresses to their
2280        respective delegation fees.
2281
2282        Args:
2283            netuid: The network UID to filter the delegation fees.
2284
2285        Returns:
2286            A dictionary mapping key addresses to their delegation fees.
2287
2288        Raises:
2289            QueryError: If the query to the network fails or is invalid.
2290        """
2291
2292        return self.query_map(
2293            "DelegationFee", [netuid], extract_value=extract_value
2294        )["DelegationFee"]

Retrieves a mapping of delegation fees for keys on the network.

Queries the network to obtain a mapping of key addresses to their respective delegation fees.

Arguments:
  • netuid: The network UID to filter the delegation fees.
Returns:

A dictionary mapping key addresses to their delegation fees.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_tempo(self, extract_value: bool = False) -> dict[int, int]:
2296    def query_map_tempo(self, extract_value: bool = False) -> dict[int, int]:
2297        """
2298        Retrieves a mapping of tempo settings for the network.
2299
2300        Queries the network to obtain the tempo (rate of reward distributions)
2301        settings for various network subnets.
2302
2303        Returns:
2304            A dictionary mapping network UIDs to their tempo settings.
2305
2306        Raises:
2307            QueryError: If the query to the network fails or is invalid.
2308        """
2309
2310        return self.query_map("Tempo", extract_value=extract_value)["Tempo"]

Retrieves a mapping of tempo settings for the network.

Queries the network to obtain the tempo (rate of reward distributions) settings for various network subnets.

Returns:

A dictionary mapping network UIDs to their tempo settings.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_immunity_period(self, extract_value: bool) -> dict[int, int]:
2312    def query_map_immunity_period(self, extract_value: bool) -> dict[int, int]:
2313        """
2314        Retrieves a mapping of immunity periods for the network.
2315
2316        Queries the network for the immunity period settings,
2317        which represent the time duration during which modules
2318        can not get deregistered.
2319
2320        Returns:
2321            A dictionary mapping network UIDs to their immunity period settings.
2322
2323        Raises:
2324            QueryError: If the query to the network fails or is invalid.
2325        """
2326
2327        return self.query_map("ImmunityPeriod", extract_value=extract_value)[
2328            "ImmunityPeriod"
2329        ]

Retrieves a mapping of immunity periods for the network.

Queries the network for the immunity period settings, which represent the time duration during which modules can not get deregistered.

Returns:

A dictionary mapping network UIDs to their immunity period settings.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_min_allowed_weights(self, extract_value: bool = False) -> dict[int, int]:
2331    def query_map_min_allowed_weights(
2332        self, extract_value: bool = False
2333    ) -> dict[int, int]:
2334        """
2335        Retrieves a mapping of minimum allowed weights for the network.
2336
2337        Queries the network to obtain the minimum allowed weights,
2338        which are the lowest permissible weight values that can be set by
2339        validators.
2340
2341        Returns:
2342            A dictionary mapping network UIDs to
2343            their minimum allowed weight values.
2344
2345        Raises:
2346            QueryError: If the query to the network fails or is invalid.
2347        """
2348
2349        return self.query_map("MinAllowedWeights", extract_value=extract_value)[
2350            "MinAllowedWeights"
2351        ]

Retrieves a mapping of minimum allowed weights for the network.

Queries the network to obtain the minimum allowed weights, which are the lowest permissible weight values that can be set by validators.

Returns:

A dictionary mapping network UIDs to their minimum allowed weight values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_max_allowed_weights(self, extract_value: bool = False) -> dict[int, int]:
2353    def query_map_max_allowed_weights(
2354        self, extract_value: bool = False
2355    ) -> dict[int, int]:
2356        """
2357        Retrieves a mapping of maximum allowed weights for the network.
2358
2359        Queries the network for the maximum allowed weights,
2360        which are the highest permissible
2361        weight values that can be set by validators.
2362
2363        Returns:
2364            A dictionary mapping network UIDs to
2365            their maximum allowed weight values.
2366
2367        Raises:
2368            QueryError: If the query to the network fails or is invalid.
2369        """
2370
2371        return self.query_map("MaxAllowedWeights", extract_value=extract_value)[
2372            "MaxAllowedWeights"
2373        ]

Retrieves a mapping of maximum allowed weights for the network.

Queries the network for the maximum allowed weights, which are the highest permissible weight values that can be set by validators.

Returns:

A dictionary mapping network UIDs to their maximum allowed weight values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_max_allowed_uids(self, extract_value: bool = False) -> dict[int, int]:
2375    def query_map_max_allowed_uids(
2376        self, extract_value: bool = False
2377    ) -> dict[int, int]:
2378        """
2379        Queries the network for the maximum number of allowed user IDs (UIDs)
2380        for each network subnet.
2381
2382        Fetches a mapping of network subnets to their respective
2383        limits on the number of user IDs that can be created or used.
2384
2385        Returns:
2386            A dictionary mapping network UIDs (unique identifiers) to their
2387            maximum allowed number of UIDs.
2388            Each entry represents a network subnet
2389            with its corresponding UID limit.
2390
2391        Raises:
2392            QueryError: If the query to the network fails or is invalid.
2393        """
2394
2395        return self.query_map("MaxAllowedUids", extract_value=extract_value)[
2396            "MaxAllowedUids"
2397        ]

Queries the network for the maximum number of allowed user IDs (UIDs) for each network subnet.

Fetches a mapping of network subnets to their respective limits on the number of user IDs that can be created or used.

Returns:

A dictionary mapping network UIDs (unique identifiers) to their maximum allowed number of UIDs. Each entry represents a network subnet with its corresponding UID limit.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_min_stake(self, extract_value: bool = False) -> dict[int, int]:
2399    def query_map_min_stake(
2400        self, extract_value: bool = False
2401    ) -> dict[int, int]:
2402        """
2403        Retrieves a mapping of minimum allowed stake on the network.
2404
2405        Queries the network to obtain the minimum number of stake,
2406        which is represented in nanotokens.
2407
2408        Returns:
2409            A dictionary mapping network UIDs to
2410            their minimum allowed stake values.
2411
2412        Raises:
2413            QueryError: If the query to the network fails or is invalid.
2414        """
2415
2416        return self.query_map("MinStake", extract_value=extract_value)[
2417            "MinStake"
2418        ]

Retrieves a mapping of minimum allowed stake on the network.

Queries the network to obtain the minimum number of stake, which is represented in nanotokens.

Returns:

A dictionary mapping network UIDs to their minimum allowed stake values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_max_stake(self, extract_value: bool = False) -> dict[int, int]:
2420    def query_map_max_stake(
2421        self, extract_value: bool = False
2422    ) -> dict[int, int]:
2423        """
2424        Retrieves a mapping of the maximum stake values for the network.
2425
2426        Queries the network for the maximum stake values across various s
2427        ubnets of the network.
2428
2429        Returns:
2430            A dictionary mapping network UIDs to their maximum stake values.
2431
2432        Raises:
2433            QueryError: If the query to the network fails or is invalid.
2434        """
2435
2436        return self.query_map("MaxStake", extract_value=extract_value)[
2437            "MaxStake"
2438        ]

Retrieves a mapping of the maximum stake values for the network.

Queries the network for the maximum stake values across various s ubnets of the network.

Returns:

A dictionary mapping network UIDs to their maximum stake values.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_founder(self, extract_value: bool = False) -> dict[int, str]:
2440    def query_map_founder(self, extract_value: bool = False) -> dict[int, str]:
2441        """
2442        Retrieves a mapping of founders for the network.
2443
2444        Queries the network to obtain the founders associated with
2445        various subnets.
2446
2447        Returns:
2448            A dictionary mapping network UIDs to their respective founders.
2449
2450        Raises:
2451            QueryError: If the query to the network fails or is invalid.
2452        """
2453
2454        return self.query_map("Founder", extract_value=extract_value)["Founder"]

Retrieves a mapping of founders for the network.

Queries the network to obtain the founders associated with various subnets.

Returns:

A dictionary mapping network UIDs to their respective founders.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_founder_share(self, extract_value: bool = False) -> dict[int, int]:
2456    def query_map_founder_share(
2457        self, extract_value: bool = False
2458    ) -> dict[int, int]:
2459        """
2460        Retrieves a mapping of founder shares for the network.
2461
2462        Queries the network for the share percentages
2463        allocated to founders across different subnets.
2464
2465        Returns:
2466            A dictionary mapping network UIDs to their founder share percentages.
2467
2468        Raises:
2469            QueryError: If the query to the network fails or is invalid.
2470        """
2471
2472        return self.query_map("FounderShare", extract_value=extract_value)[
2473            "FounderShare"
2474        ]

Retrieves a mapping of founder shares for the network.

Queries the network for the share percentages allocated to founders across different subnets.

Returns:

A dictionary mapping network UIDs to their founder share percentages.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_incentive_ratio(self, extract_value: bool = False) -> dict[int, int]:
2476    def query_map_incentive_ratio(
2477        self, extract_value: bool = False
2478    ) -> dict[int, int]:
2479        """
2480        Retrieves a mapping of incentive ratios for the network.
2481
2482        Queries the network for the incentive ratios,
2483        which are the proportions of rewards or incentives
2484        allocated in different subnets of the network.
2485
2486        Returns:
2487            A dictionary mapping network UIDs to their incentive ratios.
2488
2489        Raises:
2490            QueryError: If the query to the network fails or is invalid.
2491        """
2492
2493        return self.query_map("IncentiveRatio", extract_value=extract_value)[
2494            "IncentiveRatio"
2495        ]

Retrieves a mapping of incentive ratios for the network.

Queries the network for the incentive ratios, which are the proportions of rewards or incentives allocated in different subnets of the network.

Returns:

A dictionary mapping network UIDs to their incentive ratios.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_trust_ratio(self, extract_value: bool = False) -> dict[int, int]:
2497    def query_map_trust_ratio(
2498        self, extract_value: bool = False
2499    ) -> dict[int, int]:
2500        """
2501        Retrieves a mapping of trust ratios for the network.
2502
2503        Queries the network for trust ratios,
2504        indicative of the level of trust or credibility assigned
2505        to different subnets of the network.
2506
2507        Returns:
2508            A dictionary mapping network UIDs to their trust ratios.
2509
2510        Raises:
2511            QueryError: If the query to the network fails or is invalid.
2512        """
2513
2514        return self.query_map("TrustRatio", extract_value=extract_value)[
2515            "TrustRatio"
2516        ]

Retrieves a mapping of trust ratios for the network.

Queries the network for trust ratios, indicative of the level of trust or credibility assigned to different subnets of the network.

Returns:

A dictionary mapping network UIDs to their trust ratios.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_vote_mode_subnet(self, extract_value: bool = False) -> dict[int, str]:
2518    def query_map_vote_mode_subnet(
2519        self, extract_value: bool = False
2520    ) -> dict[int, str]:
2521        """
2522        Retrieves a mapping of vote modes for subnets within the network.
2523
2524        Queries the network for the voting modes used in different
2525        subnets, which define the methodology or approach of voting within those
2526        subnets.
2527
2528        Returns:
2529            A dictionary mapping network UIDs to their vote
2530            modes for subnets.
2531
2532        Raises:
2533            QueryError: If the query to the network fails or is invalid.
2534        """
2535
2536        return self.query_map("VoteModeSubnet", extract_value=extract_value)[
2537            "VoteModeSubnet"
2538        ]

Retrieves a mapping of vote modes for subnets within the network.

Queries the network for the voting modes used in different subnets, which define the methodology or approach of voting within those subnets.

Returns:

A dictionary mapping network UIDs to their vote modes for subnets.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_legit_whitelist( self, extract_value: bool = False) -> dict[communex.types.Ss58Address, int]:
2540    def query_map_legit_whitelist(
2541        self, extract_value: bool = False
2542    ) -> dict[Ss58Address, int]:
2543        """
2544        Retrieves a mapping of whitelisted addresses for the network.
2545
2546        Queries the network for a mapping of whitelisted addresses
2547        and their respective legitimacy status.
2548
2549        Returns:
2550            A dictionary mapping addresses to their legitimacy status.
2551
2552        Raises:
2553            QueryError: If the query to the network fails or is invalid.
2554        """
2555
2556        return self.query_map(
2557            "LegitWhitelist",
2558            module="GovernanceModule",
2559            extract_value=extract_value,
2560        )["LegitWhitelist"]

Retrieves a mapping of whitelisted addresses for the network.

Queries the network for a mapping of whitelisted addresses and their respective legitimacy status.

Returns:

A dictionary mapping addresses to their legitimacy status.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_subnet_names(self, extract_value: bool = False) -> dict[int, str]:
2562    def query_map_subnet_names(
2563        self, extract_value: bool = False
2564    ) -> dict[int, str]:
2565        """
2566        Retrieves a mapping of subnet names within the network.
2567
2568        Queries the network for the names of various subnets,
2569        providing an overview of the different
2570        subnets within the network.
2571
2572        Returns:
2573            A dictionary mapping network UIDs to their subnet names.
2574
2575        Raises:
2576            QueryError: If the query to the network fails or is invalid.
2577        """
2578
2579        return self.query_map("SubnetNames", extract_value=extract_value)[
2580            "SubnetNames"
2581        ]

Retrieves a mapping of subnet names within the network.

Queries the network for the names of various subnets, providing an overview of the different subnets within the network.

Returns:

A dictionary mapping network UIDs to their subnet names.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_balances( self, extract_value: bool = False, block_hash: str | None = None) -> dict[str, dict[str, int | dict[str, int | float]]]:
2583    def query_map_balances(
2584        self, extract_value: bool = False, block_hash: str | None = None
2585    ) -> dict[str, dict[str, int | dict[str, int | float]]]:
2586        """
2587        Retrieves a mapping of account balances within the network.
2588
2589        Queries the network for the balances associated with different accounts.
2590        It provides detailed information including various types of
2591        balances for each account.
2592
2593        Returns:
2594            A dictionary mapping account addresses to their balance details.
2595
2596        Raises:
2597            QueryError: If the query to the network fails or is invalid.
2598        """
2599
2600        return self.query_map(
2601            "Account",
2602            module="System",
2603            extract_value=extract_value,
2604            block_hash=block_hash,
2605        )["Account"]

Retrieves a mapping of account balances within the network.

Queries the network for the balances associated with different accounts. It provides detailed information including various types of balances for each account.

Returns:

A dictionary mapping account addresses to their balance details.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_registration_blocks(self, netuid: int = 0, extract_value: bool = False) -> dict[int, int]:
2607    def query_map_registration_blocks(
2608        self, netuid: int = 0, extract_value: bool = False
2609    ) -> dict[int, int]:
2610        """
2611        Retrieves a mapping of registration blocks for UIDs on the network.
2612
2613        Queries the network to find the block numbers at which various
2614        UIDs were registered.
2615
2616        Args:
2617            netuid: The network UID from which to get the registrations.
2618
2619        Returns:
2620            A dictionary mapping UIDs to their registration block numbers.
2621
2622        Raises:
2623            QueryError: If the query to the network fails or is invalid.
2624        """
2625
2626        return self.query_map(
2627            "RegistrationBlock", [netuid], extract_value=extract_value
2628        )["RegistrationBlock"]

Retrieves a mapping of registration blocks for UIDs on the network.

Queries the network to find the block numbers at which various UIDs were registered.

Arguments:
  • netuid: The network UID from which to get the registrations.
Returns:

A dictionary mapping UIDs to their registration block numbers.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def query_map_name(self, netuid: int = 0, extract_value: bool = False) -> dict[int, str]:
2630    def query_map_name(
2631        self, netuid: int = 0, extract_value: bool = False
2632    ) -> dict[int, str]:
2633        """
2634        Retrieves a mapping of names for keys on the network.
2635
2636        Queries the network for the names associated with different keys.
2637        It provides a mapping of key UIDs to their registered names.
2638
2639        Args:
2640            netuid: The network UID from which to get the names.
2641
2642        Returns:
2643            A dictionary mapping key UIDs to their names.
2644
2645        Raises:
2646            QueryError: If the query to the network fails or is invalid.
2647        """
2648
2649        return self.query_map("Name", [netuid], extract_value=extract_value)[
2650            "Name"
2651        ]

Retrieves a mapping of names for keys on the network.

Queries the network for the names associated with different keys. It provides a mapping of key UIDs to their registered names.

Arguments:
  • netuid: The network UID from which to get the names.
Returns:

A dictionary mapping key UIDs to their names.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_immunity_period(self, netuid: int = 0) -> int:
2655    def get_immunity_period(self, netuid: int = 0) -> int:
2656        """
2657        Queries the network for the immunity period setting.
2658
2659        The immunity period is a time duration during which a module
2660        can not be deregistered from the network.
2661        Fetches the immunity period for a specified network subnet.
2662
2663        Args:
2664            netuid: The network UID for which to query the immunity period.
2665
2666        Returns:
2667            The immunity period setting for the specified network subnet.
2668
2669        Raises:
2670            QueryError: If the query to the network fails or is invalid.
2671        """
2672
2673        return self.query(
2674            "ImmunityPeriod",
2675            params=[netuid],
2676        )

Queries the network for the immunity period setting.

The immunity period is a time duration during which a module can not be deregistered from the network. Fetches the immunity period for a specified network subnet.

Arguments:
  • netuid: The network UID for which to query the immunity period.
Returns:

The immunity period setting for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_set_weights_per_epoch(self):
2678    def get_max_set_weights_per_epoch(self):
2679        return self.query("MaximumSetWeightCallsPerEpoch")
def get_min_allowed_weights(self, netuid: int = 0) -> int:
2681    def get_min_allowed_weights(self, netuid: int = 0) -> int:
2682        """
2683        Queries the network for the minimum allowed weights setting.
2684
2685        Retrieves the minimum weight values that are possible to set
2686        by a validator within a specific network subnet.
2687
2688        Args:
2689            netuid: The network UID for which to query the minimum allowed
2690              weights.
2691
2692        Returns:
2693            The minimum allowed weight values for the specified network
2694              subnet.
2695
2696        Raises:
2697            QueryError: If the query to the network fails or is invalid.
2698        """
2699
2700        return self.query(
2701            "MinAllowedWeights",
2702            params=[netuid],
2703        )

Queries the network for the minimum allowed weights setting.

Retrieves the minimum weight values that are possible to set by a validator within a specific network subnet.

Arguments:
  • netuid: The network UID for which to query the minimum allowed weights.
Returns:

The minimum allowed weight values for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_dao_treasury_address(self) -> communex.types.Ss58Address:
2705    def get_dao_treasury_address(self) -> Ss58Address:
2706        return self.query("DaoTreasuryAddress", module="GovernanceModule")
def get_max_allowed_weights(self, netuid: int = 0) -> int:
2708    def get_max_allowed_weights(self, netuid: int = 0) -> int:
2709        """
2710        Queries the network for the maximum allowed weights setting.
2711
2712        Retrieves the maximum weight values that are possible to set
2713        by a validator within a specific network subnet.
2714
2715        Args:
2716            netuid: The network UID for which to query the maximum allowed
2717              weights.
2718
2719        Returns:
2720            The maximum allowed weight values for the specified network
2721              subnet.
2722
2723        Raises:
2724            QueryError: If the query to the network fails or is invalid.
2725        """
2726
2727        return self.query("MaxAllowedWeights", params=[netuid])

Queries the network for the maximum allowed weights setting.

Retrieves the maximum weight values that are possible to set by a validator within a specific network subnet.

Arguments:
  • netuid: The network UID for which to query the maximum allowed weights.
Returns:

The maximum allowed weight values for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_allowed_uids(self, netuid: int = 0) -> int:
2729    def get_max_allowed_uids(self, netuid: int = 0) -> int:
2730        """
2731        Queries the network for the maximum allowed UIDs setting.
2732
2733        Fetches the upper limit on the number of user IDs that can
2734        be allocated or used within a specific network subnet.
2735
2736        Args:
2737            netuid: The network UID for which to query the maximum allowed UIDs.
2738
2739        Returns:
2740            The maximum number of allowed UIDs for the specified network subnet.
2741
2742        Raises:
2743            QueryError: If the query to the network fails or is invalid.
2744        """
2745
2746        return self.query("MaxAllowedUids", params=[netuid])

Queries the network for the maximum allowed UIDs setting.

Fetches the upper limit on the number of user IDs that can be allocated or used within a specific network subnet.

Arguments:
  • netuid: The network UID for which to query the maximum allowed UIDs.
Returns:

The maximum number of allowed UIDs for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_name(self, netuid: int = 0) -> str:
2748    def get_name(self, netuid: int = 0) -> str:
2749        """
2750        Queries the network for the name of a specific subnet.
2751
2752        Args:
2753            netuid: The network UID for which to query the name.
2754
2755        Returns:
2756            The name of the specified network subnet.
2757
2758        Raises:
2759            QueryError: If the query to the network fails or is invalid.
2760        """
2761
2762        return self.query("Name", params=[netuid])

Queries the network for the name of a specific subnet.

Arguments:
  • netuid: The network UID for which to query the name.
Returns:

The name of the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_subnet_name(self, netuid: int = 0) -> str:
2764    def get_subnet_name(self, netuid: int = 0) -> str:
2765        """
2766        Queries the network for the name of a specific subnet.
2767
2768        Args:
2769            netuid: The network UID for which to query the name.
2770
2771        Returns:
2772            The name of the specified network subnet.
2773
2774        Raises:
2775            QueryError: If the query to the network fails or is invalid.
2776        """
2777
2778        return self.query("SubnetNames", params=[netuid])

Queries the network for the name of a specific subnet.

Arguments:
  • netuid: The network UID for which to query the name.
Returns:

The name of the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_global_dao_treasury(self):
2780    def get_global_dao_treasury(self):
2781        return self.query("GlobalDaoTreasury", module="GovernanceModule")
def get_n(self, netuid: int = 0) -> int:
2783    def get_n(self, netuid: int = 0) -> int:
2784        """
2785        Queries the network for the 'N' hyperparameter, which represents how
2786        many modules are on the network.
2787
2788        Args:
2789            netuid: The network UID for which to query the 'N' hyperparameter.
2790
2791        Returns:
2792            The value of the 'N' hyperparameter for the specified network
2793              subnet.
2794
2795        Raises:
2796            QueryError: If the query to the network fails or is invalid.
2797        """
2798
2799        return self.query("N", params=[netuid])

Queries the network for the 'N' hyperparameter, which represents how many modules are on the network.

Arguments:
  • netuid: The network UID for which to query the 'N' hyperparameter.
Returns:

The value of the 'N' hyperparameter for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_tempo(self, netuid: int = 0) -> int:
2801    def get_tempo(self, netuid: int = 0) -> int:
2802        """
2803        Queries the network for the tempo setting, measured in blocks, for the
2804        specified subnet.
2805
2806        Args:
2807            netuid: The network UID for which to query the tempo.
2808
2809        Returns:
2810            The tempo setting for the specified subnet.
2811
2812        Raises:
2813            QueryError: If the query to the network fails or is invalid.
2814        """
2815
2816        return self.query("Tempo", params=[netuid])

Queries the network for the tempo setting, measured in blocks, for the specified subnet.

Arguments:
  • netuid: The network UID for which to query the tempo.
Returns:

The tempo setting for the specified subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_total_free_issuance(self, block_hash: str | None = None) -> int:
2818    def get_total_free_issuance(self, block_hash: str | None = None) -> int:
2819        """
2820        Queries the network for the total free issuance.
2821
2822        Fetches the total amount of free issuance tokens available
2823
2824        Returns:
2825            The total free issuance amount.
2826
2827        Raises:
2828            QueryError: If the query to the network fails or is invalid.
2829        """
2830
2831        return self.query(
2832            "TotalIssuance", module="Balances", block_hash=block_hash
2833        )

Queries the network for the total free issuance.

Fetches the total amount of free issuance tokens available

Returns:

The total free issuance amount.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_total_stake(self, block_hash: str | None = None) -> int:
2835    def get_total_stake(self, block_hash: str | None = None) -> int:
2836        """
2837        Retrieves a mapping of total stakes for keys on the network.
2838
2839        Queries the network for a mapping of key UIDs to their total stake amounts.
2840
2841        Returns:
2842            A dictionary mapping key UIDs to their total stake amounts.
2843
2844        Raises:
2845            QueryError: If the query to the network fails or is invalid.
2846        """
2847
2848        return self.query("TotalStake", block_hash=block_hash)

Retrieves a mapping of total stakes for keys on the network.

Queries the network for a mapping of key UIDs to their total stake amounts.

Returns:

A dictionary mapping key UIDs to their total stake amounts.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_registrations_per_block(self):
2850    def get_registrations_per_block(self):
2851        """
2852        Queries the network for the number of registrations per block.
2853
2854        Fetches the number of registrations that are processed per
2855        block within the network.
2856
2857        Returns:
2858            The number of registrations processed per block.
2859
2860        Raises:
2861            QueryError: If the query to the network fails or is invalid.
2862        """
2863
2864        return self.query(
2865            "RegistrationsPerBlock",
2866        )

Queries the network for the number of registrations per block.

Fetches the number of registrations that are processed per block within the network.

Returns:

The number of registrations processed per block.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def max_registrations_per_block(self, netuid: int = 0):
2868    def max_registrations_per_block(self, netuid: int = 0):
2869        """
2870        Queries the network for the maximum number of registrations per block.
2871
2872        Retrieves the upper limit of registrations that can be processed in
2873        each block within a specific network subnet.
2874
2875        Args:
2876            netuid: The network UID for which to query.
2877
2878        Returns:
2879            The maximum number of registrations per block for
2880            the specified network subnet.
2881
2882        Raises:
2883            QueryError: If the query to the network fails or is invalid.
2884        """
2885
2886        return self.query(
2887            "MaxRegistrationsPerBlock",
2888            params=[netuid],
2889        )

Queries the network for the maximum number of registrations per block.

Retrieves the upper limit of registrations that can be processed in each block within a specific network subnet.

Arguments:
  • netuid: The network UID for which to query.
Returns:

The maximum number of registrations per block for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_proposal(self, proposal_id: int = 0):
2891    def get_proposal(self, proposal_id: int = 0):
2892        """
2893        Queries the network for a specific proposal.
2894
2895        Args:
2896            proposal_id: The ID of the proposal to query.
2897
2898        Returns:
2899            The details of the specified proposal.
2900
2901        Raises:
2902            QueryError: If the query to the network fails, is invalid,
2903                or if the proposal ID does not exist.
2904        """
2905
2906        return self.query(
2907            "Proposals",
2908            params=[proposal_id],
2909        )

Queries the network for a specific proposal.

Arguments:
  • proposal_id: The ID of the proposal to query.
Returns:

The details of the specified proposal.

Raises:
  • QueryError: If the query to the network fails, is invalid, or if the proposal ID does not exist.
def get_trust(self, netuid: int = 0):
2911    def get_trust(self, netuid: int = 0):
2912        """
2913        Queries the network for the trust setting of a specific network subnet.
2914
2915        Retrieves the trust level or score, which may represent the
2916        level of trustworthiness or reliability within a
2917        particular network subnet.
2918
2919        Args:
2920            netuid: The network UID for which to query the trust setting.
2921
2922        Returns:
2923            The trust level or score for the specified network subnet.
2924
2925        Raises:
2926            QueryError: If the query to the network fails or is invalid.
2927        """
2928
2929        return self.query(
2930            "Trust",
2931            params=[netuid],
2932        )

Queries the network for the trust setting of a specific network subnet.

Retrieves the trust level or score, which may represent the level of trustworthiness or reliability within a particular network subnet.

Arguments:
  • netuid: The network UID for which to query the trust setting.
Returns:

The trust level or score for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_uids(self, key: communex.types.Ss58Address, netuid: int = 0) -> bool | None:
2934    def get_uids(self, key: Ss58Address, netuid: int = 0) -> bool | None:
2935        """
2936        Queries the network for module UIDs associated with a specific key.
2937
2938        Args:
2939            key: The key address for which to query UIDs.
2940            netuid: The network UID within which to search for the key.
2941
2942        Returns:
2943            A list of UIDs associated with the specified key.
2944
2945        Raises:
2946            QueryError: If the query to the network fails or is invalid.
2947        """
2948
2949        return self.query(
2950            "Uids",
2951            params=[netuid, key],
2952        )

Queries the network for module UIDs associated with a specific key.

Arguments:
  • key: The key address for which to query UIDs.
  • netuid: The network UID within which to search for the key.
Returns:

A list of UIDs associated with the specified key.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_unit_emission(self) -> int:
2954    def get_unit_emission(self) -> int:
2955        """
2956        Queries the network for the unit emission setting.
2957
2958        Retrieves the unit emission value, which represents the
2959        emission rate or quantity for the $COMM token.
2960
2961        Returns:
2962            The unit emission value in nanos for the network.
2963
2964        Raises:
2965            QueryError: If the query to the network fails or is invalid.
2966        """
2967
2968        return self.query("UnitEmission", module="SubnetEmissionModule")

Queries the network for the unit emission setting.

Retrieves the unit emission value, which represents the emission rate or quantity for the $COMM token.

Returns:

The unit emission value in nanos for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_tx_rate_limit(self) -> int:
2970    def get_tx_rate_limit(self) -> int:
2971        """
2972        Queries the network for the transaction rate limit.
2973
2974        Retrieves the rate limit for transactions within the network,
2975        which defines the maximum number of transactions that can be
2976        processed within a certain timeframe.
2977
2978        Returns:
2979            The transaction rate limit for the network.
2980
2981        Raises:
2982            QueryError: If the query to the network fails or is invalid.
2983        """
2984
2985        return self.query(
2986            "TxRateLimit",
2987        )

Queries the network for the transaction rate limit.

Retrieves the rate limit for transactions within the network, which defines the maximum number of transactions that can be processed within a certain timeframe.

Returns:

The transaction rate limit for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_subnet_burn(self) -> int:
2989    def get_subnet_burn(self) -> int:
2990        """Queries the network for the subnet burn value.
2991
2992        Retrieves the subnet burn value from the network, which represents
2993        the amount of tokens that are burned (permanently removed from
2994        circulation) for subnet-related operations.
2995
2996        Returns:
2997            int: The subnet burn value.
2998
2999        Raises:
3000            QueryError: If the query to the network fails or returns invalid data.
3001        """
3002
3003        return self.query(
3004            "SubnetBurn",
3005        )

Queries the network for the subnet burn value.

Retrieves the subnet burn value from the network, which represents the amount of tokens that are burned (permanently removed from circulation) for subnet-related operations.

Returns:

int: The subnet burn value.

Raises:
  • QueryError: If the query to the network fails or returns invalid data.
def get_burn_rate(self) -> int:
3007    def get_burn_rate(self) -> int:
3008        """
3009        Queries the network for the burn rate setting.
3010
3011        Retrieves the burn rate, which represents the rate at
3012        which the $COMM token is permanently
3013        removed or 'burned' from circulation.
3014
3015        Returns:
3016            The burn rate for the network.
3017
3018        Raises:
3019            QueryError: If the query to the network fails or is invalid.
3020        """
3021
3022        return self.query(
3023            "BurnRate",
3024            params=[],
3025        )

Queries the network for the burn rate setting.

Retrieves the burn rate, which represents the rate at which the $COMM token is permanently removed or 'burned' from circulation.

Returns:

The burn rate for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_burn(self, netuid: int = 0) -> int:
3027    def get_burn(self, netuid: int = 0) -> int:
3028        """
3029        Queries the network for the burn setting.
3030
3031        Retrieves the burn value, which represents the amount of the
3032        $COMM token that is 'burned' or permanently removed from
3033        circulation.
3034
3035        Args:
3036            netuid: The network UID for which to query the burn value.
3037
3038        Returns:
3039            The burn value for the specified network subnet.
3040
3041        Raises:
3042            QueryError: If the query to the network fails or is invalid.
3043        """
3044
3045        return self.query("Burn", params=[netuid])

Queries the network for the burn setting.

Retrieves the burn value, which represents the amount of the $COMM token that is 'burned' or permanently removed from circulation.

Arguments:
  • netuid: The network UID for which to query the burn value.
Returns:

The burn value for the specified network subnet.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_min_burn(self) -> int:
3047    def get_min_burn(self) -> int:
3048        """
3049        Queries the network for the minimum burn setting.
3050
3051        Retrieves the minimum burn value, indicating the lowest
3052        amount of the $COMM tokens that can be 'burned' or
3053        permanently removed from circulation.
3054
3055        Returns:
3056            The minimum burn value for the network.
3057
3058        Raises:
3059            QueryError: If the query to the network fails or is invalid.
3060        """
3061
3062        return self.query(
3063            "BurnConfig",
3064            params=[],
3065        )["min_burn"]

Queries the network for the minimum burn setting.

Retrieves the minimum burn value, indicating the lowest amount of the $COMM tokens that can be 'burned' or permanently removed from circulation.

Returns:

The minimum burn value for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_min_weight_stake(self) -> int:
3067    def get_min_weight_stake(self) -> int:
3068        """
3069        Queries the network for the minimum weight stake setting.
3070
3071        Retrieves the minimum weight stake, which represents the lowest
3072        stake weight that is allowed for certain operations or
3073        transactions within the network.
3074
3075        Returns:
3076            The minimum weight stake for the network.
3077
3078        Raises:
3079            QueryError: If the query to the network fails or is invalid.
3080        """
3081
3082        return self.query("MinWeightStake", params=[])

Queries the network for the minimum weight stake setting.

Retrieves the minimum weight stake, which represents the lowest stake weight that is allowed for certain operations or transactions within the network.

Returns:

The minimum weight stake for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_vote_mode_global(self) -> str:
3084    def get_vote_mode_global(self) -> str:
3085        """
3086        Queries the network for the global vote mode setting.
3087
3088        Retrieves the global vote mode, which defines the overall voting
3089        methodology or approach used across the network in default.
3090
3091        Returns:
3092            The global vote mode setting for the network.
3093
3094        Raises:
3095            QueryError: If the query to the network fails or is invalid.
3096        """
3097
3098        return self.query(
3099            "VoteModeGlobal",
3100        )

Queries the network for the global vote mode setting.

Retrieves the global vote mode, which defines the overall voting methodology or approach used across the network in default.

Returns:

The global vote mode setting for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_proposals(self) -> int:
3102    def get_max_proposals(self) -> int:
3103        """
3104        Queries the network for the maximum number of proposals allowed.
3105
3106        Retrieves the upper limit on the number of proposals that can be
3107        active or considered at any given time within the network.
3108
3109        Returns:
3110            The maximum number of proposals allowed on the network.
3111
3112        Raises:
3113            QueryError: If the query to the network fails or is invalid.
3114        """
3115
3116        return self.query(
3117            "MaxProposals",
3118        )

Queries the network for the maximum number of proposals allowed.

Retrieves the upper limit on the number of proposals that can be active or considered at any given time within the network.

Returns:

The maximum number of proposals allowed on the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_registrations_per_block(self) -> int:
3120    def get_max_registrations_per_block(self) -> int:
3121        """
3122        Queries the network for the maximum number of registrations per block.
3123
3124        Retrieves the maximum number of registrations that can
3125        be processed in each block within the network.
3126
3127        Returns:
3128            The maximum number of registrations per block on the network.
3129
3130        Raises:
3131            QueryError: If the query to the network fails or is invalid.
3132        """
3133
3134        return self.query(
3135            "MaxRegistrationsPerBlock",
3136            params=[],
3137        )

Queries the network for the maximum number of registrations per block.

Retrieves the maximum number of registrations that can be processed in each block within the network.

Returns:

The maximum number of registrations per block on the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_name_length(self) -> int:
3139    def get_max_name_length(self) -> int:
3140        """
3141        Queries the network for the maximum length allowed for names.
3142
3143        Retrieves the maximum character length permitted for names
3144        within the network. Such as the module names
3145
3146        Returns:
3147            The maximum length allowed for names on the network.
3148
3149        Raises:
3150            QueryError: If the query to the network fails or is invalid.
3151        """
3152
3153        return self.query(
3154            "MaxNameLength",
3155            params=[],
3156        )

Queries the network for the maximum length allowed for names.

Retrieves the maximum character length permitted for names within the network. Such as the module names

Returns:

The maximum length allowed for names on the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_global_vote_threshold(self) -> int:
3158    def get_global_vote_threshold(self) -> int:
3159        """
3160        Queries the network for the global vote threshold.
3161
3162        Retrieves the global vote threshold, which is the critical value or
3163        percentage required for decisions in the network's governance process.
3164
3165        Returns:
3166            The global vote threshold for the network.
3167
3168        Raises:
3169            QueryError: If the query to the network fails or is invalid.
3170        """
3171
3172        return self.query(
3173            "GlobalVoteThreshold",
3174        )

Queries the network for the global vote threshold.

Retrieves the global vote threshold, which is the critical value or percentage required for decisions in the network's governance process.

Returns:

The global vote threshold for the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_allowed_subnets(self) -> int:
3176    def get_max_allowed_subnets(self) -> int:
3177        """
3178        Queries the network for the maximum number of allowed subnets.
3179
3180        Retrieves the upper limit on the number of subnets that can
3181        be created or operated within the network.
3182
3183        Returns:
3184            The maximum number of allowed subnets on the network.
3185
3186        Raises:
3187            QueryError: If the query to the network fails or is invalid.
3188        """
3189
3190        return self.query(
3191            "MaxAllowedSubnets",
3192            params=[],
3193        )

Queries the network for the maximum number of allowed subnets.

Retrieves the upper limit on the number of subnets that can be created or operated within the network.

Returns:

The maximum number of allowed subnets on the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_max_allowed_modules(self) -> int:
3195    def get_max_allowed_modules(self) -> int:
3196        """
3197        Queries the network for the maximum number of allowed modules.
3198
3199        Retrieves the upper limit on the number of modules that
3200        can be registered within the network.
3201
3202        Returns:
3203            The maximum number of allowed modules on the network.
3204
3205        Raises:
3206            QueryError: If the query to the network fails or is invalid.
3207        """
3208
3209        return self.query(
3210            "MaxAllowedModules",
3211            params=[],
3212        )

Queries the network for the maximum number of allowed modules.

Retrieves the upper limit on the number of modules that can be registered within the network.

Returns:

The maximum number of allowed modules on the network.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_min_stake(self, netuid: int = 0) -> int:
3214    def get_min_stake(self, netuid: int = 0) -> int:
3215        """
3216        Queries the network for the minimum stake required to register a key.
3217
3218        Retrieves the minimum amount of stake necessary for
3219        registering a key within a specific network subnet.
3220
3221        Args:
3222            netuid: The network UID for which to query the minimum stake.
3223
3224        Returns:
3225            The minimum stake required for key registration in nanos.
3226
3227        Raises:
3228            QueryError: If the query to the network fails or is invalid.
3229        """
3230
3231        return self.query("MinStake", params=[netuid])

Queries the network for the minimum stake required to register a key.

Retrieves the minimum amount of stake necessary for registering a key within a specific network subnet.

Arguments:
  • netuid: The network UID for which to query the minimum stake.
Returns:

The minimum stake required for key registration in nanos.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_stakefrom(self, key: communex.types.Ss58Address) -> dict[str, int]:
3233    def get_stakefrom(
3234        self,
3235        key: Ss58Address,
3236    ) -> dict[str, int]:
3237        """
3238        Retrieves the stake amounts from all stakers to a specific staked address.
3239
3240        Queries the network for the stakes received by a particular staked address
3241        from all stakers.
3242
3243        Args:
3244            key: The address of the key receiving the stakes.
3245
3246        Returns:
3247            A dictionary mapping staker addresses to their respective stake amounts.
3248
3249        Raises:
3250            QueryError: If the query to the network fails or is invalid.
3251        """
3252
3253        # Has to use query map in order to iterate through the storage prefix.
3254        return self.query_map("StakeFrom", [key], extract_value=False).get(
3255            "StakeFrom", {}
3256        )

Retrieves the stake amounts from all stakers to a specific staked address.

Queries the network for the stakes received by a particular staked address from all stakers.

Arguments:
  • key: The address of the key receiving the stakes.
Returns:

A dictionary mapping staker addresses to their respective stake amounts.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_staketo(self, key: communex.types.Ss58Address) -> dict[str, int]:
3258    def get_staketo(
3259        self,
3260        key: Ss58Address,
3261    ) -> dict[str, int]:
3262        """
3263        Retrieves the stake amounts provided by a specific staker to all staked addresses.
3264
3265        Queries the network for the stakes provided by a particular staker to
3266        all staked addresses.
3267
3268        Args:
3269            key: The address of the key providing the stakes.
3270
3271        Returns:
3272            A dictionary mapping staked addresses to their respective received stake amounts.
3273
3274        Raises:
3275            QueryError: If the query to the network fails or is invalid.
3276        """
3277
3278        # Has to use query map in order to iterate through the storage prefix.
3279        return self.query_map("StakeTo", [key], extract_value=False).get(
3280            "StakeTo", {}
3281        )

Retrieves the stake amounts provided by a specific staker to all staked addresses.

Queries the network for the stakes provided by a particular staker to all staked addresses.

Arguments:
  • key: The address of the key providing the stakes.
Returns:

A dictionary mapping staked addresses to their respective received stake amounts.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_balance(self, addr: communex.types.Ss58Address) -> int:
3283    def get_balance(
3284        self,
3285        addr: Ss58Address,
3286    ) -> int:
3287        """
3288        Retrieves the balance of a specific key.
3289
3290        Args:
3291            addr: The address of the key to query the balance for.
3292
3293        Returns:
3294            The balance of the specified key.
3295
3296        Raises:
3297            QueryError: If the query to the network fails or is invalid.
3298        """
3299
3300        result = self.query("Account", module="System", params=[addr])
3301
3302        return result["data"]["free"]

Retrieves the balance of a specific key.

Arguments:
  • addr: The address of the key to query the balance for.
Returns:

The balance of the specified key.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_block( self, block_hash: str | None = None) -> dict[typing.Any, typing.Any] | None:
3304    def get_block(self, block_hash: str | None = None) -> dict[Any, Any] | None:
3305        """
3306        Retrieves information about a specific block in the network.
3307
3308        Queries the network for details about a block, such as its number,
3309        hash, and other relevant information.
3310
3311        Returns:
3312            The requested information about the block,
3313            or None if the block does not exist
3314            or the information is not available.
3315
3316        Raises:
3317            QueryError: If the query to the network fails or is invalid.
3318        """
3319
3320        with self.get_conn() as substrate:
3321            block: dict[Any, Any] | None = substrate.get_block(  # type: ignore
3322                block_hash  # type: ignore
3323            )
3324
3325        return block

Retrieves information about a specific block in the network.

Queries the network for details about a block, such as its number, hash, and other relevant information.

Returns:

The requested information about the block, or None if the block does not exist or the information is not available.

Raises:
  • QueryError: If the query to the network fails or is invalid.
def get_existential_deposit(self, block_hash: str | None = None) -> int:
3327    def get_existential_deposit(self, block_hash: str | None = None) -> int:
3328        """
3329        Retrieves the existential deposit value for the network.
3330
3331        The existential deposit is the minimum balance that must be maintained
3332        in an account to prevent it from being purged. Denotated in nano units.
3333
3334        Returns:
3335            The existential deposit value in nano units.
3336        Note:
3337            The value returned is a fixed value defined in the
3338            client and may not reflect changes in the network's configuration.
3339        """
3340
3341        with self.get_conn() as substrate:
3342            result: int = substrate.get_constant(  #  type: ignore
3343                "Balances", "ExistentialDeposit", block_hash
3344            ).value  #  type: ignore
3345
3346        return result

Retrieves the existential deposit value for the network.

The existential deposit is the minimum balance that must be maintained in an account to prevent it from being purged. Denotated in nano units.

Returns:

The existential deposit value in nano units.

Note:

The value returned is a fixed value defined in the client and may not reflect changes in the network's configuration.

def get_voting_power_delegators(self) -> list[communex.types.Ss58Address]:
3348    def get_voting_power_delegators(self) -> list[Ss58Address]:
3349        result = self.query(
3350            "NotDelegatingVotingPower", [], module="GovernanceModule"
3351        )
3352        return result
def add_transfer_dao_treasury_proposal( self, key: substrateinterface.keypair.Keypair, data: str, amount_nano: int, dest: communex.types.Ss58Address):
3354    def add_transfer_dao_treasury_proposal(
3355        self,
3356        key: Keypair,
3357        data: str,
3358        amount_nano: int,
3359        dest: Ss58Address,
3360    ):
3361        params = {"dest": dest, "value": amount_nano, "data": data}
3362
3363        return self.compose_call(
3364            module="GovernanceModule",
3365            fn="add_transfer_dao_treasury_proposal",
3366            params=params,
3367            key=key,
3368        )