diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index d199c029fc..d3b6d9bac7 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -56,6 +56,7 @@ ) from bittensor.core.extrinsics.asyncex.coldkey_swap import ( announce_coldkey_swap_extrinsic, + clear_coldkey_swap_announcement_extrinsic, dispute_coldkey_swap_extrinsic, swap_coldkey_announced_extrinsic, ) @@ -7115,6 +7116,50 @@ async def dispute_coldkey_swap( wait_for_revealed_execution=wait_for_revealed_execution, ) + async def clear_coldkey_swap_announcement( + self, + wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, + period: Optional[int] = DEFAULT_PERIOD, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, + ) -> ExtrinsicResponse: + """ + Clears (withdraws) a pending coldkey swap announcement. + + Callable by the coldkey that has an active, undisputed swap announcement. The reannouncement delay must have + elapsed past the execution block before the announcement can be cleared. + + Parameters: + wallet: Bittensor wallet object (should be the current coldkey with an active announcement). + mev_protection: If ``True``, encrypts and submits the transaction through the MEV Shield pallet. + period: The number of blocks during which the transaction will remain valid. + raise_error: Raises a relevant exception rather than returning ``False`` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution if mev_protection used. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Notes: + - The coldkey must have an active, undisputed swap announcement. + - The reannouncement delay must have elapsed past the execution block. + """ + return await clear_coldkey_swap_announcement_extrinsic( + subtensor=self, + wallet=wallet, + mev_protection=mev_protection, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, + ) + async def dissolve_crowdloan( self, wallet: "Wallet", diff --git a/bittensor/core/extrinsics/asyncex/coldkey_swap.py b/bittensor/core/extrinsics/asyncex/coldkey_swap.py index db44be4253..67ac802051 100644 --- a/bittensor/core/extrinsics/asyncex/coldkey_swap.py +++ b/bittensor/core/extrinsics/asyncex/coldkey_swap.py @@ -184,6 +184,75 @@ async def dispute_coldkey_swap_extrinsic( return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) +async def clear_coldkey_swap_announcement_extrinsic( + subtensor: "AsyncSubtensor", + wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, +) -> ExtrinsicResponse: + """ + Clears (withdraws) a pending coldkey swap announcement. + + Callable by the coldkey that has an active, undisputed swap announcement. The reannouncement delay must have + elapsed past the execution block before the announcement can be cleared. + + Parameters: + subtensor: AsyncSubtensor instance with the connection to the chain. + wallet: Bittensor wallet object (should be the current coldkey with an active announcement). + mev_protection: If ``True``, encrypts and submits the transaction through the MEV Shield pallet. + period: The number of blocks during which the transaction will remain valid. + raise_error: Raises a relevant exception rather than returning ``False`` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution if mev_protection used. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Notes: + - The coldkey must have an active, undisputed swap announcement. + - The reannouncement delay must have elapsed past the execution block. + """ + try: + if not ( + unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error) + ).success: + return unlocked + + call = await SubtensorModule(subtensor).clear_coldkey_swap_announcement() + + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) + + return response + + except Exception as error: + return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) + + async def swap_coldkey_announced_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", diff --git a/bittensor/core/extrinsics/coldkey_swap.py b/bittensor/core/extrinsics/coldkey_swap.py index dcd695075f..d391261730 100644 --- a/bittensor/core/extrinsics/coldkey_swap.py +++ b/bittensor/core/extrinsics/coldkey_swap.py @@ -186,6 +186,75 @@ def dispute_coldkey_swap_extrinsic( return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) +def clear_coldkey_swap_announcement_extrinsic( + subtensor: "Subtensor", + wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, +) -> ExtrinsicResponse: + """ + Clears (withdraws) a pending coldkey swap announcement. + + Callable by the coldkey that has an active, undisputed swap announcement. The reannouncement delay must have + elapsed past the execution block before the announcement can be cleared. + + Parameters: + subtensor: Subtensor instance with the connection to the chain. + wallet: Bittensor wallet object (should be the current coldkey with an active announcement). + mev_protection: If `True`, encrypts and submits the transaction through the MEV Shield pallet. + period: The number of blocks during which the transaction will remain valid. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution if mev_protection used. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Notes: + - The coldkey must have an active, undisputed swap announcement. + - The reannouncement delay must have elapsed past the execution block. + """ + try: + if not ( + unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error) + ).success: + return unlocked + + call = SubtensorModule(subtensor).clear_coldkey_swap_announcement() + + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) + + return response + + except Exception as error: + return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) + + def swap_coldkey_announced_extrinsic( subtensor: "Subtensor", wallet: "Wallet", diff --git a/bittensor/core/extrinsics/pallets/subtensor_module.py b/bittensor/core/extrinsics/pallets/subtensor_module.py index 0da15461cb..a54d73b13a 100644 --- a/bittensor/core/extrinsics/pallets/subtensor_module.py +++ b/bittensor/core/extrinsics/pallets/subtensor_module.py @@ -366,6 +366,17 @@ def dispute_coldkey_swap(self) -> Call: """ return self.create_composed_call() + def clear_coldkey_swap_announcement(self) -> Call: + """Returns GenericCall instance for Subtensor function SubtensorModule.clear_coldkey_swap_announcement. + + Callable by the coldkey that has an active swap announcement. Withdraws the announcement + after the reannouncement delay has elapsed past the execution block. + + Returns: + GenericCall instance. + """ + return self.create_composed_call() + def reset_coldkey_swap(self, coldkey: str) -> Call: """Returns GenericCall instance for Subtensor function SubtensorModule.reset_coldkey_swap. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 53f594e4f8..4a0994cdba 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -55,6 +55,7 @@ ) from bittensor.core.extrinsics.coldkey_swap import ( announce_coldkey_swap_extrinsic, + clear_coldkey_swap_announcement_extrinsic, dispute_coldkey_swap_extrinsic, swap_coldkey_announced_extrinsic, ) @@ -5924,6 +5925,50 @@ def dispute_coldkey_swap( wait_for_revealed_execution=wait_for_revealed_execution, ) + def clear_coldkey_swap_announcement( + self, + wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, + period: Optional[int] = DEFAULT_PERIOD, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, + ) -> ExtrinsicResponse: + """ + Clears (withdraws) a pending coldkey swap announcement. + + Callable by the coldkey that has an active, undisputed swap announcement. The reannouncement delay must have + elapsed past the execution block before the announcement can be cleared. + + Parameters: + wallet: Bittensor wallet object (should be the current coldkey with an active announcement). + mev_protection: If `True`, encrypts and submits the transaction through the MEV Shield pallet. + period: The number of blocks during which the transaction will remain valid. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution if mev_protection used. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Notes: + - The coldkey must have an active, undisputed swap announcement. + - The reannouncement delay must have elapsed past the execution block. + """ + return clear_coldkey_swap_announcement_extrinsic( + subtensor=self, + wallet=wallet, + mev_protection=mev_protection, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, + ) + def dissolve_crowdloan( self, wallet: "Wallet", diff --git a/bittensor/extras/subtensor_api/extrinsics.py b/bittensor/extras/subtensor_api/extrinsics.py index 43fafc79ad..d9a3e59f84 100644 --- a/bittensor/extras/subtensor_api/extrinsics.py +++ b/bittensor/extras/subtensor_api/extrinsics.py @@ -13,6 +13,7 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.add_stake_burn = subtensor.add_stake_burn self.add_stake_multiple = subtensor.add_stake_multiple self.announce_coldkey_swap = subtensor.announce_coldkey_swap + self.clear_coldkey_swap_announcement = subtensor.clear_coldkey_swap_announcement self.dispute_coldkey_swap = subtensor.dispute_coldkey_swap self.burned_register = subtensor.burned_register self.claim_root = subtensor.claim_root diff --git a/tests/e2e_tests/test_coldkey_swap.py b/tests/e2e_tests/test_coldkey_swap.py index 926c7f6ff0..0cd3613176 100644 --- a/tests/e2e_tests/test_coldkey_swap.py +++ b/tests/e2e_tests/test_coldkey_swap.py @@ -38,15 +38,24 @@ def test_coldkey_swap(subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_ - Step 2: Attempt to execute other transaction (transfer) from announced coldkey - Step 3: Verify transaction is blocked (except swap_coldkey_announced) - 5. Dispute and root reset: + 5. Clear announcement: + - Step 1: Announce swap + - Step 2: Attempt to clear too early (should fail) + - Step 3: Wait for clear block (execution_block + reannouncement_delay) + - Step 4: Clear the announcement + - Step 5: Verify announcement is removed + - Step 6: Verify transfers are unblocked after clear + + 6. Dispute and root reset: - Step 1: Dave announces swap, then disputes it (dispute_coldkey_swap) - Step 2: Verify dispute is recorded (get_coldkey_swap_dispute) - Step 3: Verify account is blocked (transfer fails) - - Step 4: Root resets coldkey swap (reset_coldkey_swap) - - Step 5: Verify dispute and announcement are cleared - - Step 6: Verify transfers are unblocked after reset + - Step 4: Verify clear is blocked while disputed + - Step 5: Root resets coldkey swap (reset_coldkey_swap) + - Step 6: Verify dispute and announcement are cleared + - Step 7: Verify transfers are unblocked after reset - 6. Root swap override: + 7. Root swap override: - Step 1: Root swaps Dave to Charlie without announcement - Step 2: Verify announcement and dispute are cleared - Step 3: Verify old coldkey is reaped @@ -434,7 +443,74 @@ def assert_coldkey_reaped(coldkey_ss58: str, label: str) -> None: ) assert response.success, f"Failed to fund Dave: {response.message}" - # === 5. Dispute and root reset === + # === 5. Clear announcement === + logging.console.info("Testing clear announcement") + + # Step 1: Alice announces swap to Bob + logging.console.info("Step 1: Alice announces swap to Bob") + existing_announcement = subtensor.wallets.get_coldkey_swap_announcement( + coldkey_ss58=alice_wallet.coldkeypub.ss58_address + ) + assert existing_announcement is None, ( + "No announcement should exist before clear test" + ) + response = subtensor.extrinsics.announce_coldkey_swap( + wallet=alice_wallet, + new_coldkey_ss58=bob_wallet.coldkeypub.ss58_address, + ) + assert response.success, f"Failed to announce swap: {response.message}" + announcement = subtensor.wallets.get_coldkey_swap_announcement( + coldkey_ss58=alice_wallet.coldkeypub.ss58_address + ) + assert announcement is not None, "Announcement should exist" + + # Step 2: Attempt to clear too early (before clear_block) + logging.console.info("Step 2: Attempting clear too early (should fail)") + clear_block = announcement.execution_block + reannouncement_delay + current_block = subtensor.chain.get_current_block() + assert current_block < clear_block, "Current block should be before clear block" + response = subtensor.extrinsics.clear_coldkey_swap_announcement( + wallet=alice_wallet, + raise_error=False, + ) + assert not response.success, "Clear should fail before clear block" + logging.console.info("Clear too early correctly rejected") + + # Step 3: Wait for clear_block (execution_block + reannouncement_delay) + logging.console.info( + f"Step 3: Waiting for clear block {clear_block} " + f"(execution={announcement.execution_block} + reannounce_delay={reannouncement_delay})" + ) + subtensor.wait_for_block(clear_block) + + # Step 4: Clear the announcement + logging.console.info("Step 4: Clearing announcement") + response = subtensor.extrinsics.clear_coldkey_swap_announcement( + wallet=alice_wallet, + ) + assert response.success, f"Failed to clear announcement: {response.message}" + + # Step 5: Verify announcement is removed + logging.console.info("Step 5: Verify announcement is removed") + announcement_after_clear = subtensor.wallets.get_coldkey_swap_announcement( + coldkey_ss58=alice_wallet.coldkeypub.ss58_address + ) + assert announcement_after_clear is None, ( + "Announcement should be removed after clear" + ) + + # Step 6: Verify transfers work again + logging.console.info("Step 6: Verify transfers are unblocked after clear") + response = subtensor.extrinsics.transfer( + wallet=alice_wallet, + destination_ss58=charlie_wallet.coldkeypub.ss58_address, + amount=Balance.from_tao(1), + raise_error=False, + ) + assert response.success, "Transfer should be allowed after clear" + logging.console.info("Clear announcement test completed successfully") + + # === 6. Dispute and root reset === logging.console.info("Testing dispute and root reset") # Step 1: Dave announces swap to Charlie @@ -478,6 +554,14 @@ def assert_coldkey_reaped(coldkey_ss58: str, label: str) -> None: raise_error=False, ) assert not response.success, "Transfer should be blocked while disputed" + + # Step 4b: Verify clear is also blocked while disputed + logging.console.info("Step 4b: Verify clear is blocked while disputed") + response = subtensor.extrinsics.clear_coldkey_swap_announcement( + wallet=dave_wallet, + raise_error=False, + ) + assert not response.success, "Clear should be blocked while disputed" logging.console.info("Account blocking verified") # Step 5: Root resets the coldkey swap (alice_wallet is //Alice, root) @@ -513,7 +597,7 @@ def assert_coldkey_reaped(coldkey_ss58: str, label: str) -> None: assert response.success, "Transfer should be allowed after reset" logging.console.info("Dispute scenario completed successfully") - # === 6. Root swap override === + # === 7. Root swap override === logging.console.info("Testing root swap override") # Ensure Dave has enough balance for root swap cost @@ -584,15 +668,24 @@ async def test_coldkey_swap_async( - Step 2: Attempt to execute other transaction (transfer) from announced coldkey - Step 3: Verify transaction is blocked (except swap_coldkey_announced) - 5. Dispute and root reset: + 5. Clear announcement: + - Step 1: Announce swap + - Step 2: Attempt to clear too early (should fail) + - Step 3: Wait for clear block (execution_block + reannouncement_delay) + - Step 4: Clear the announcement + - Step 5: Verify announcement is removed + - Step 6: Verify transfers are unblocked after clear + + 6. Dispute and root reset: - Step 1: Dave announces swap, then disputes it (dispute_coldkey_swap) - Step 2: Verify dispute is recorded (get_coldkey_swap_dispute) - Step 3: Verify account is blocked (transfer fails) - - Step 4: Root resets coldkey swap (reset_coldkey_swap) - - Step 5: Verify dispute and announcement are cleared - - Step 6: Verify transfers are unblocked after reset + - Step 4: Verify clear is blocked while disputed + - Step 5: Root resets coldkey swap (reset_coldkey_swap) + - Step 6: Verify dispute and announcement are cleared + - Step 7: Verify transfers are unblocked after reset - 6. Root swap override: + 7. Root swap override: - Step 1: Root swaps Dave to Charlie without announcement - Step 2: Verify announcement and dispute are cleared - Step 3: Verify old coldkey is reaped @@ -996,7 +1089,76 @@ async def assert_coldkey_reaped(coldkey_ss58: str, label: str) -> None: ) assert response.success, f"Failed to fund Dave: {response.message}" - # === 5. Dispute and root reset === + # === 5. Clear announcement === + logging.console.info("Testing clear announcement") + + # Step 1: Alice announces swap to Bob + logging.console.info("Step 1: Alice announces swap to Bob") + existing_announcement = await async_subtensor.wallets.get_coldkey_swap_announcement( + coldkey_ss58=alice_wallet.coldkeypub.ss58_address + ) + assert existing_announcement is None, ( + "No announcement should exist before clear test" + ) + response = await async_subtensor.extrinsics.announce_coldkey_swap( + wallet=alice_wallet, + new_coldkey_ss58=bob_wallet.coldkeypub.ss58_address, + ) + assert response.success, f"Failed to announce swap: {response.message}" + announcement = await async_subtensor.wallets.get_coldkey_swap_announcement( + coldkey_ss58=alice_wallet.coldkeypub.ss58_address + ) + assert announcement is not None, "Announcement should exist" + + # Step 2: Attempt to clear too early (before clear_block) + logging.console.info("Step 2: Attempting clear too early (should fail)") + clear_block = announcement.execution_block + reannouncement_delay + current_block = await async_subtensor.chain.get_current_block() + assert current_block < clear_block, "Current block should be before clear block" + response = await async_subtensor.extrinsics.clear_coldkey_swap_announcement( + wallet=alice_wallet, + raise_error=False, + ) + assert not response.success, "Clear should fail before clear block" + logging.console.info("Clear too early correctly rejected") + + # Step 3: Wait for clear_block (execution_block + reannouncement_delay) + logging.console.info( + f"Step 3: Waiting for clear block {clear_block} " + f"(execution={announcement.execution_block} + reannounce_delay={reannouncement_delay})" + ) + await async_subtensor.wait_for_block(clear_block) + + # Step 4: Clear the announcement + logging.console.info("Step 4: Clearing announcement") + response = await async_subtensor.extrinsics.clear_coldkey_swap_announcement( + wallet=alice_wallet, + ) + assert response.success, f"Failed to clear announcement: {response.message}" + + # Step 5: Verify announcement is removed + logging.console.info("Step 5: Verify announcement is removed") + announcement_after_clear = ( + await async_subtensor.wallets.get_coldkey_swap_announcement( + coldkey_ss58=alice_wallet.coldkeypub.ss58_address + ) + ) + assert announcement_after_clear is None, ( + "Announcement should be removed after clear" + ) + + # Step 6: Verify transfers work again + logging.console.info("Step 6: Verify transfers are unblocked after clear") + response = await async_subtensor.extrinsics.transfer( + wallet=alice_wallet, + destination_ss58=charlie_wallet.coldkeypub.ss58_address, + amount=Balance.from_tao(1), + raise_error=False, + ) + assert response.success, "Transfer should be allowed after clear" + logging.console.info("Clear announcement test completed successfully") + + # === 6. Dispute and root reset === logging.console.info("Testing dispute and root reset") # Step 1: Dave announces swap to Charlie @@ -1040,6 +1202,14 @@ async def assert_coldkey_reaped(coldkey_ss58: str, label: str) -> None: raise_error=False, ) assert not response.success, "Transfer should be blocked while disputed" + + # Step 4b: Verify clear is also blocked while disputed + logging.console.info("Step 4b: Verify clear is blocked while disputed") + response = await async_subtensor.extrinsics.clear_coldkey_swap_announcement( + wallet=dave_wallet, + raise_error=False, + ) + assert not response.success, "Clear should be blocked while disputed" logging.console.info("Account blocking verified") # Step 5: Root resets the coldkey swap (alice_wallet is //Alice, root) @@ -1077,7 +1247,7 @@ async def assert_coldkey_reaped(coldkey_ss58: str, label: str) -> None: assert response.success, "Transfer should be allowed after reset" logging.console.info("Dispute scenario completed successfully") - # === 6. Root swap override === + # === 7. Root swap override === logging.console.info("Testing root swap override") # Ensure Dave has enough balance for root swap cost diff --git a/tests/unit_tests/extrinsics/test_coldkey_swap.py b/tests/unit_tests/extrinsics/test_coldkey_swap.py index 9ef59d549d..28b280124c 100644 --- a/tests/unit_tests/extrinsics/test_coldkey_swap.py +++ b/tests/unit_tests/extrinsics/test_coldkey_swap.py @@ -371,6 +371,50 @@ def test_dispute_coldkey_swap_extrinsic(subtensor, mocker): assert response == mocked_sign_and_send_extrinsic.return_value +def test_clear_coldkey_swap_announcement_extrinsic(subtensor, mocker): + """Verify that sync clear_coldkey_swap_announcement_extrinsic calls pallet and sign_and_send_extrinsic.""" + wallet = mocker.MagicMock(spec=Wallet) + + mocked_unlock_wallet = mocker.patch.object( + ExtrinsicResponse, + "unlock_wallet", + return_value=ExtrinsicResponse(success=True, message="Unlocked"), + ) + mocked_subtensor_module = mocker.patch.object( + coldkey_swap, "SubtensorModule", return_value=mocker.MagicMock() + ) + mocked_pallet_instance = mocked_subtensor_module.return_value + mocked_pallet_instance.clear_coldkey_swap_announcement.return_value = ( + mocker.MagicMock() + ) + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), + ) + + # Call + response = coldkey_swap.clear_coldkey_swap_announcement_extrinsic( + subtensor=subtensor, + wallet=wallet, + mev_protection=False, + ) + + # Asserts + mocked_unlock_wallet.assert_called_once_with(wallet, False) + mocked_subtensor_module.assert_called_once_with(subtensor) + mocked_pallet_instance.clear_coldkey_swap_announcement.assert_called_once_with() + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_pallet_instance.clear_coldkey_swap_announcement.return_value, + wallet=wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + period=None, + raise_error=False, + ) + assert response == mocked_sign_and_send_extrinsic.return_value + + def test_reset_coldkey_swap_extrinsic(subtensor, mocker): """Verify that sync reset_coldkey_swap_extrinsic uses sudo_call_extrinsic.""" wallet = mocker.MagicMock(spec=Wallet) @@ -457,6 +501,14 @@ def test_subtensor_module_dispute_reset_swap_coldkey_call_names(subtensor, mocke call_params={}, ) + mocked_compose_call.reset_mock() + pallet.clear_coldkey_swap_announcement() + mocked_compose_call.assert_called_with( + call_module="SubtensorModule", + call_function="clear_coldkey_swap_announcement", + call_params={}, + ) + mocked_compose_call.reset_mock() pallet.reset_coldkey_swap( coldkey="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"