diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 926c593ac0..e4c4a1c547 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -21,6 +21,7 @@ NeuronInfo, ProposalVoteData, SubnetHyperparameters, + SubnetIdentity, SubnetInfo, WeightCommitInfo, decode_account_id, @@ -34,6 +35,7 @@ burned_register_extrinsic, register_extrinsic, register_subnet_extrinsic, + set_subnet_identity_extrinsic, ) from bittensor.core.extrinsics.asyncex.move_stake import ( transfer_stake_extrinsic, @@ -2581,8 +2583,8 @@ async def wait_for_block(self, block: Optional[int] = None): bool: True if the target block was reached, False if timeout occurred. Example: - >>> await subtensor.wait_for_block() # Waits for next block - >>> await subtensor.wait_for_block(block=1234) # Waits for specific block + await subtensor.wait_for_block() # Waits for next block + await subtensor.wait_for_block(block=1234) # Waits for specific block """ async def handler(block_data: dict): @@ -3188,6 +3190,44 @@ async def root_set_weights( wait_for_inclusion=wait_for_inclusion, ) + async def set_subnet_identity( + self, + wallet: "Wallet", + netuid: int, + subnet_identity: SubnetIdentity, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, + ) -> tuple[bool, str]: + """ + Sets the identity of a subnet for a specific wallet and network. + + Arguments: + wallet (Wallet): The wallet instance that will authorize the transaction. + netuid (int): The unique ID of the network on which the operation takes place. + subnet_identity (SubnetIdentity): The identity data of the subnet including attributes like name, GitHub + repository, contact, URL, discord, description, and any additional metadata. + wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. + wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. + + Returns: + tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the + operation, and the second element is a message providing additional information. + """ + return await set_subnet_identity_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_identity.subnet_name, + github_repo=subnet_identity.github_repo, + subnet_contact=subnet_identity.subnet_contact, + subnet_url=subnet_identity.subnet_url, + discord=subnet_identity.discord, + description=subnet_identity.description, + additional=subnet_identity.additional, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + async def set_weights( self, wallet: "Wallet", diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 599f13ab5c..070b55e904 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -430,10 +430,9 @@ async def register_subnet_extrinsic( if not wait_for_finalization and not wait_for_inclusion: return True - await response.process_events() if not await response.is_success: logging.error( - f"Failed to register subnet: {format_error_message(await response.error_message, subtensor.substrate)}" + f"Failed to register subnet: {format_error_message(await response.error_message)}" ) return False @@ -441,3 +440,82 @@ async def register_subnet_extrinsic( ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) return True + + +async def set_subnet_identity_extrinsic( + subtensor: "AsyncSubtensor", + wallet: "Wallet", + netuid: int, + subnet_name: str, + github_repo: str, + subnet_contact: str, + subnet_url: str, + discord: str, + description: str, + additional: str, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, +) -> tuple[bool, str]: + """ + Set the identity information for a given subnet. + + Arguments: + subtensor (AsyncSubtensor): An instance of the Subtensor class to interact with the blockchain. + wallet (Wallet): A wallet instance used to sign and submit the extrinsic. + netuid (int): The unique ID for the subnet. + subnet_name (str): The name of the subnet to assign the identity information. + github_repo (str): URL of the GitHub repository related to the subnet. + subnet_contact (str): Subnet's contact information, e.g., email or contact link. + subnet_url (str): The URL of the subnet's primary web portal. + discord (str): Discord server or contact for the subnet. + description (str): A textual description of the subnet. + additional (str): Any additional metadata or information related to the subnet. + wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). + wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). + + Returns: + tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second + element contains a descriptive message. + """ + + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False, unlock.message + + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + + response = await subtensor.substrate.submit_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if not wait_for_finalization and not wait_for_inclusion: + return True, f"Identities for subnet {netuid} are sent to the chain." + + if await response.is_success: + logging.success( + f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" + ) + return True, f"Identities for subnet {netuid} are set." + else: + error_message = await response.error_message + logging.error( + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {error_message}" + ) + return False, f"Failed to set identity for subnet {netuid}: {error_message}" diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 8505eab15b..c1feea3a30 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -424,3 +424,81 @@ def register_extrinsic( # Failed to register after max attempts. logging.error("[red]No more attempts.[/red]") return False + + +def set_subnet_identity_extrinsic( + subtensor: "Subtensor", + wallet: "Wallet", + netuid: int, + subnet_name: str, + github_repo: str, + subnet_contact: str, + subnet_url: str, + discord: str, + description: str, + additional: str, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, +) -> tuple[bool, str]: + """ + Set the identity information for a given subnet. + + Arguments: + subtensor (Subtensor): An instance of the Subtensor class to interact with the blockchain. + wallet (Wallet): A wallet instance used to sign and submit the extrinsic. + netuid (int): The unique ID for the subnet. + subnet_name (str): The name of the subnet to assign the identity information. + github_repo (str): URL of the GitHub repository related to the subnet. + subnet_contact (str): Subnet's contact information, e.g., email or contact link. + subnet_url (str): The URL of the subnet's primary web portal. + discord (str): Discord server or contact for the subnet. + description (str): A textual description of the subnet. + additional (str): Any additional metadata or information related to the subnet. + wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). + wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). + + Returns: + tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second + element contains a descriptive message. + """ + + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False, unlock.message + + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if not wait_for_finalization and not wait_for_inclusion: + return True, f"Identities for subnet {netuid} are sent to the chain." + + if success: + logging.success( + f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" + ) + return True, f"Identities for subnet {netuid} are set." + else: + logging.error( + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" + ) + return False, f"Failed to set identity for subnet {netuid}: {message}" diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 690af504b7..15ce88b691 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -21,6 +21,7 @@ StakeInfo, SubnetHyperparameters, WeightCommitInfo, + SubnetIdentity, SubnetInfo, decode_account_id, ) @@ -40,6 +41,7 @@ burned_register_extrinsic, register_extrinsic, register_subnet_extrinsic, + set_subnet_identity_extrinsic, ) from bittensor.core.extrinsics.root import ( root_register_extrinsic, @@ -2526,6 +2528,44 @@ def root_set_weights( wait_for_inclusion=wait_for_inclusion, ) + def set_subnet_identity( + self, + wallet: "Wallet", + netuid: int, + subnet_identity: SubnetIdentity, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, + ) -> tuple[bool, str]: + """ + Sets the identity of a subnet for a specific wallet and network. + + Arguments: + wallet (Wallet): The wallet instance that will authorize the transaction. + netuid (int): The unique ID of the network on which the operation takes place. + subnet_identity (SubnetIdentity): The identity data of the subnet including attributes like name, GitHub + repository, contact, URL, discord, description, and any additional metadata. + wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. + wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. + + Returns: + tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the + operation, and the second element is a message providing additional information. + """ + return set_subnet_identity_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_identity.subnet_name, + github_repo=subnet_identity.github_repo, + subnet_contact=subnet_identity.subnet_contact, + subnet_url=subnet_identity.subnet_url, + discord=subnet_identity.discord, + description=subnet_identity.description, + additional=subnet_identity.additional, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + def set_weights( self, wallet: "Wallet", diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index e3f2bf0ef0..985aee1ed8 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -32,16 +32,27 @@ from bittensor.core.axon import Axon from bittensor.core.chain_data import ( # noqa: F401 AxonInfo, + ChainIdentity, + DelegateInfo, + DelegateInfoLite, + DynamicInfo, + IPInfo, + MetagraphInfo, + MetagraphInfoEmissions, + MetagraphInfoParams, + MetagraphInfoPool, NeuronInfo, NeuronInfoLite, PrometheusInfo, - DelegateInfo, - StakeInfo, - SubnetInfo, - SubnetHyperparameters, - IPInfo, ProposalCallData, ProposalVoteData, + ScheduledColdkeySwapInfo, + StakeInfo, + SubnetHyperparameters, + SubnetIdentity, + SubnetInfo, + SubnetState, + WeightCommitInfo, ) from bittensor.core.config import ( # noqa: F401 InvalidConfigFile, diff --git a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py new file mode 100644 index 0000000000..e60cc69db3 --- /dev/null +++ b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py @@ -0,0 +1,104 @@ +import pytest + +from bittensor.core.chain_data import SubnetIdentity +from bittensor.utils.btlogging import logging + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_happy_pass(subtensor, alice_wallet): + logging.console.info( + "[magenta]Testing `set_subnet_identity_extrinsic` with success result.[/magenta]" + ) + + netuid = 2 + + # Register a subnet, netuid 2 + assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" + + # Verify subnet created successfully + assert subtensor.subnet_exists(netuid), "Subnet wasn't created successfully" + + # make sure subnet_identity is empty + assert ( + subtensor.subnet(netuid).subnet_identity is None + ), "Subnet identity should be None before set" + + # prepare SubnetIdentity for subnet + subnet_identity = SubnetIdentity( + subnet_name="e2e test subnet", + github_repo="e2e test repo", + subnet_contact="e2e test contact", + subnet_url="e2e test url", + discord="e2e test discord", + description="e2e test description", + additional="e2e test additional", + ) + + # set SubnetIdentity to subnet + assert ( + subtensor.set_subnet_identity( + wallet=alice_wallet, + netuid=netuid, + subnet_identity=subnet_identity, + )[0] + is True + ), "Set subnet identity failed" + + # check SubnetIdentity of the subnet + assert subtensor.subnet(netuid).subnet_identity == subnet_identity + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_failed( + subtensor, alice_wallet, bob_wallet +): + """ + Test case for verifying the behavior of the `set_subnet_identity_extrinsic` function in the + scenario where the result of the function is expected to fail. It ensures proper handling + and validation when attempting to set the subnet identity under specific conditions. + + Args: + subtensor: The instance of the subtensor class under test. + alice_wallet: A mock or test wallet associated with Alice, used for creating a subnet. + bob_wallet: A mock or test wallet associated with Bob, used for setting the subnet identity. + + Decorators: + @pytest.mark.asyncio: Marks this test as an asynchronous test. + """ + logging.console.info( + "[magenta]Testing `set_subnet_identity_extrinsic` with failed result.[/magenta]" + ) + + netuid = 2 + + # Register a subnet, netuid 2 + assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" + + # Verify subnet created successfully + assert subtensor.subnet_exists(netuid), "Subnet wasn't created successfully" + + # make sure subnet_identity is empty + assert ( + subtensor.subnet(netuid).subnet_identity is None + ), "Subnet identity should be None before set" + + # prepare SubnetIdentity for subnet + subnet_identity = SubnetIdentity( + subnet_name="e2e test subnet", + github_repo="e2e test repo", + subnet_contact="e2e test contact", + subnet_url="e2e test url", + discord="e2e test discord", + description="e2e test description", + additional="e2e test additional", + ) + + # set SubnetIdentity to subnet + assert ( + subtensor.set_subnet_identity( + wallet=bob_wallet, + netuid=netuid, + subnet_identity=subnet_identity, + )[0] + is False + ), "Set subnet identity failed" diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index 82d9e6b561..5013469d1b 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -447,3 +447,134 @@ async def is_stale_side_effect(*_, **__): wait_for_finalization=True, ) assert result is False + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_is_success(subtensor, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns the correct result.""" + # Preps + wallet = mocker.MagicMock(autospec=Wallet) + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + + mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") + + fake_response = mocker.Mock() + fake_response.is_success = mocker.AsyncMock(return_value=True)() + mocked_submit_extrinsic = mocker.patch.object( + subtensor.substrate, "submit_extrinsic", return_value=fake_response + ) + + # Call + result = await async_registration.set_subnet_identity_extrinsic( + subtensor=subtensor, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + ) + + # Asserts + mocked_compose_call.assert_awaited_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + mocked_submit_extrinsic.assert_awaited_once_with( + call=mocked_compose_call.return_value, + wallet=wallet, + wait_for_inclusion=False, + wait_for_finalization=True, + ) + + assert result == (True, "Identities for subnet 123 are set.") + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_is_failed(subtensor, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns False with bad result.""" + # Preps + wallet = mocker.MagicMock(autospec=Wallet) + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + fake_error_message = "error message" + + mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") + + fake_response = mocker.Mock() + fake_response.is_success = mocker.AsyncMock(return_value=False)() + fake_response.error_message = mocker.AsyncMock(return_value=fake_error_message)() + mocked_submit_extrinsic = mocker.patch.object( + subtensor.substrate, "submit_extrinsic", return_value=fake_response + ) + + # Call + result = await async_registration.set_subnet_identity_extrinsic( + subtensor=subtensor, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Asserts + mocked_compose_call.assert_awaited_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + mocked_submit_extrinsic.assert_awaited_once_with( + call=mocked_compose_call.return_value, + wallet=wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert result == ( + False, + f"Failed to set identity for subnet {netuid}: {fake_error_message}", + ) diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index 18676619de..efe66714bf 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -222,3 +222,124 @@ def test_burned_register_extrinsic( ) # Assert assert result == expected_result, f"Test failed for test_id: {test_id}" + + +def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns the correct result.""" + # Preps + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + + mocked_compose_call = mocker.patch.object(mock_subtensor.substrate, "compose_call") + mocked_sign_and_send_extrinsic = mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + ) + + # Call + result = registration.set_subnet_identity_extrinsic( + subtensor=mock_subtensor, + wallet=mock_wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + ) + + # Asserts + mocked_compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": mock_wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": "mock_subnet_name", + "github_repo": "mock_github_repo", + "subnet_contact": "mock_subnet_contact", + "subnet_url": "mock_subnet_url", + "discord": "mock_discord", + "description": "mock_description", + "additional": "mock_additional", + }, + ) + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, + wallet=mock_wallet, + wait_for_inclusion=False, + wait_for_finalization=True, + ) + + assert result == (True, "Identities for subnet 123 are set.") + + +def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns False with bad result.""" + # Preps + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + + fake_error_message = "error message" + + mocked_compose_call = mocker.patch.object(mock_subtensor.substrate, "compose_call") + mocked_sign_and_send_extrinsic = mocker.patch.object( + mock_subtensor, + "sign_and_send_extrinsic", + return_value=(False, fake_error_message), + ) + + # Call + result = registration.set_subnet_identity_extrinsic( + subtensor=mock_subtensor, + wallet=mock_wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + ) + + # Asserts + mocked_compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": mock_wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": "mock_subnet_name", + "github_repo": "mock_github_repo", + "subnet_contact": "mock_subnet_contact", + "subnet_url": "mock_subnet_url", + "discord": "mock_discord", + "description": "mock_description", + "additional": "mock_additional", + }, + ) + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, + wallet=mock_wallet, + wait_for_inclusion=False, + wait_for_finalization=True, + ) + + assert result == ( + False, + f"Failed to set identity for subnet {netuid}: {fake_error_message}", + ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 6e88196906..10d1ba5e66 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2637,3 +2637,38 @@ async def test_get_all_subnets_info_success(mocker, subtensor): async_subtensor.SubnetInfo.list_from_dicts.assert_called_once_with( subtensor.query_runtime_api.return_value, ) + + +@pytest.mark.asyncio +async def test_set_subnet_identity(mocker, subtensor): + """Verify that subtensor method `set_subnet_identity` calls proper function with proper arguments.""" + # Preps + fake_wallet = mocker.Mock() + fake_netuid = 123 + fake_subnet_identity = mocker.MagicMock() + + mocked_extrinsic = mocker.patch.object( + async_subtensor, "set_subnet_identity_extrinsic" + ) + + # Call + result = await subtensor.set_subnet_identity( + wallet=fake_wallet, netuid=fake_netuid, subnet_identity=fake_subnet_identity + ) + + # Asserts + mocked_extrinsic.assert_awaited_once_with( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + subnet_name=fake_subnet_identity.subnet_name, + github_repo=fake_subnet_identity.github_repo, + subnet_contact=fake_subnet_identity.subnet_contact, + subnet_url=fake_subnet_identity.subnet_url, + discord=fake_subnet_identity.discord, + description=fake_subnet_identity.description, + additional=fake_subnet_identity.additional, + wait_for_finalization=True, + wait_for_inclusion=False, + ) + assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 43414ca534..385bb45501 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3050,3 +3050,37 @@ def test_connection_limit(mocker): with pytest.raises(websockets.InvalidStatus): for i in range(2): Subtensor("test") + + +def test_set_subnet_identity(mocker, subtensor): + """Verify that subtensor method `set_subnet_identity` calls proper function with proper arguments.""" + # Preps + fake_wallet = mocker.Mock() + fake_netuid = 123 + fake_subnet_identity = mocker.MagicMock() + + mocked_extrinsic = mocker.patch.object( + subtensor_module, "set_subnet_identity_extrinsic" + ) + + # Call + result = subtensor.set_subnet_identity( + wallet=fake_wallet, netuid=fake_netuid, subnet_identity=fake_subnet_identity + ) + + # Asserts + mocked_extrinsic.assert_called_once_with( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + subnet_name=fake_subnet_identity.subnet_name, + github_repo=fake_subnet_identity.github_repo, + subnet_contact=fake_subnet_identity.subnet_contact, + subnet_url=fake_subnet_identity.subnet_url, + discord=fake_subnet_identity.discord, + description=fake_subnet_identity.description, + additional=fake_subnet_identity.additional, + wait_for_finalization=True, + wait_for_inclusion=False, + ) + assert result == mocked_extrinsic.return_value