diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c78f28f165..98542c89a6 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -98,6 +98,7 @@ torch, u16_normalized_float, u64_normalized_float, + get_transfer_fn_params, ) from bittensor.core.extrinsics.asyncex.liquidity import ( add_liquidity_extrinsic, @@ -3134,7 +3135,7 @@ async def get_total_subnets( return getattr(result, "value", None) async def get_transfer_fee( - self, wallet: "Wallet", dest: str, value: Balance + self, wallet: "Wallet", dest: str, value: Balance, keep_alive: bool = True ) -> Balance: """ Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This @@ -3146,6 +3147,8 @@ async def get_transfer_fee( dest: The ``SS58`` address of the destination account. value: The amount of tokens to be transferred, specified as a Balance object, or in Tao (float) or Rao (int) units. + keep_alive: Whether the transfer fee should be calculated based on keeping the wallet alive (existential + deposit) or not. Returns: bittensor.utils.balance.Balance: The estimated transaction fee for the transfer, represented as a Balance @@ -3155,12 +3158,15 @@ async def get_transfer_fee( wallet has sufficient funds to cover both the transfer amount and the associated costs. This function provides a crucial tool for managing financial operations within the Bittensor network. """ - value = check_and_convert_to_balance(value) + if value is not None: + value = check_and_convert_to_balance(value) + call_params: dict[str, Union[int, str, bool]] + call_function, call_params = get_transfer_fn_params(value, dest, keep_alive) call = await self.substrate.compose_call( call_module="Balances", - call_function="transfer_keep_alive", - call_params={"dest": dest, "value": value.rao}, + call_function=call_function, + call_params=call_params, ) try: @@ -5455,7 +5461,7 @@ async def transfer( self, wallet: "Wallet", dest: str, - amount: Balance, + amount: Optional[Balance], transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, @@ -5468,7 +5474,7 @@ async def transfer( Arguments: wallet: Source wallet for the transfer. dest: Destination address for the transfer. - amount: Number of tokens to transfer. + amount: Number of tokens to transfer. `None` is transferring all. transfer_all: Flag to transfer all tokens. Default is `False`. wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `True`. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to `False`. @@ -5479,7 +5485,8 @@ async def transfer( Returns: `True` if the transferring was successful, otherwise `False`. """ - amount = check_and_convert_to_balance(amount) + if amount is not None: + amount = check_and_convert_to_balance(amount) return await transfer_extrinsic( subtensor=self, wallet=wallet, diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index e98e234307..6b0332cd73 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -6,6 +6,7 @@ get_explorer_url_for_network, is_valid_bittensor_address_or_public_key, unlock_key, + get_transfer_fn_params, ) from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -19,10 +20,11 @@ async def _do_transfer( subtensor: "AsyncSubtensor", wallet: "Wallet", destination: str, - amount: "Balance", + amount: Optional[Balance], wait_for_inclusion: bool = True, wait_for_finalization: bool = False, period: Optional[int] = None, + keep_alive: bool = True, ) -> tuple[bool, str, str]: """ Makes transfer from wallet to destination public key address. @@ -39,14 +41,17 @@ async def _do_transfer( period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + keep_alive (bool): If `True`, will keep the existential deposit in the account. Returns: success, block hash, formatted error message """ + call_function, call_params = get_transfer_fn_params(amount, destination, keep_alive) + call = await subtensor.substrate.compose_call( call_module="Balances", - call_function="transfer_keep_alive", - call_params={"dest": destination, "value": amount.rao}, + call_function=call_function, + call_params=call_params, ) success, message = await subtensor.sign_and_send_extrinsic( @@ -73,7 +78,7 @@ async def transfer_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", dest: str, - amount: "Balance", + amount: Optional[Balance], transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, @@ -86,7 +91,8 @@ async def transfer_extrinsic( subtensor (bittensor.core.async_subtensor.AsyncSubtensor): initialized AsyncSubtensor object used for transfer wallet (bittensor_wallet.Wallet): Bittensor wallet object to make transfer from. dest (str): Destination public key address (ss58_address or ed25519) of recipient. - amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. + amount (Optional[bittensor.utils.balance.Balance]): Amount to stake as Bittensor balance. `None` if + transferring all. transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. @@ -102,6 +108,11 @@ async def transfer_extrinsic( finalization / inclusion, the response is `True`, regardless of its inclusion. """ destination = dest + + if amount is None and not transfer_all: + logging.error("If not transferring all, `amount` must be specified.") + return False + # Validate destination address. if not is_valid_bittensor_address_or_public_key(destination): logging.error( @@ -128,7 +139,7 @@ async def transfer_extrinsic( ) fee = await subtensor.get_transfer_fee( - wallet=wallet, dest=destination, value=amount + wallet=wallet, dest=destination, value=amount, keep_alive=keep_alive ) if not keep_alive: @@ -137,12 +148,10 @@ async def transfer_extrinsic( # Check if we have enough balance. if transfer_all is True: - amount = account_balance - fee - existential_deposit - if amount < Balance(0): + if (account_balance - fee) < existential_deposit: logging.error("Not enough balance to transfer") return False - - if account_balance < (amount + fee + existential_deposit): + elif account_balance < (amount + fee + existential_deposit): logging.error(":cross_mark: [red]Not enough balance[/red]") logging.error(f"\t\tBalance:\t[blue]{account_balance}[/blue]") logging.error(f"\t\tAmount:\t[blue]{amount}[/blue]") @@ -155,6 +164,7 @@ async def transfer_extrinsic( wallet=wallet, destination=destination, amount=amount, + keep_alive=keep_alive, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, period=period, diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index 370efaed36..9faecdcea4 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -5,6 +5,7 @@ is_valid_bittensor_address_or_public_key, unlock_key, get_explorer_url_for_network, + get_transfer_fn_params, ) from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -18,10 +19,11 @@ def _do_transfer( subtensor: "Subtensor", wallet: "Wallet", destination: str, - amount: Balance, + amount: Optional[Balance], wait_for_inclusion: bool = True, wait_for_finalization: bool = False, period: Optional[int] = None, + keep_alive: bool = True, ) -> tuple[bool, str, str]: """ Makes transfer from wallet to destination public key address. @@ -30,7 +32,7 @@ def _do_transfer( subtensor (bittensor.core.subtensor.Subtensor): the Subtensor object used for transfer wallet (bittensor_wallet.Wallet): Bittensor wallet object to make transfer from. destination (str): Destination public key address (ss58_address or ed25519) of recipient. - amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. + amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. `None` if transferring all. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning @@ -38,14 +40,17 @@ def _do_transfer( period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + keep_alive (bool): If `True`, will keep the existential deposit in the account. Returns: success, block hash, formatted error message """ + call_function, call_params = get_transfer_fn_params(amount, destination, keep_alive) + call = subtensor.substrate.compose_call( call_module="Balances", - call_function="transfer_keep_alive", - call_params={"dest": destination, "value": amount.rao}, + call_function=call_function, + call_params=call_params, ) success, message = subtensor.sign_and_send_extrinsic( @@ -72,7 +77,7 @@ def transfer_extrinsic( subtensor: "Subtensor", wallet: "Wallet", dest: str, - amount: Balance, + amount: Optional[Balance], transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, @@ -85,7 +90,7 @@ def transfer_extrinsic( subtensor (bittensor.core.subtensor.Subtensor): the Subtensor object used for transfer wallet (bittensor_wallet.Wallet): Bittensor wallet object to make transfer from. dest (str): Destination public key address (ss58_address or ed25519) of recipient. - amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. + amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. `None` if transferring all. transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. @@ -100,6 +105,10 @@ def transfer_extrinsic( success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is `True`, regardless of its inclusion. """ + if amount is None and not transfer_all: + logging.error("If not transferring all, `amount` must be specified.") + return False + # Validate destination address. if not is_valid_bittensor_address_or_public_key(dest): logging.error( @@ -127,16 +136,16 @@ def transfer_extrinsic( else: existential_deposit = subtensor.get_existential_deposit(block=block) - fee = subtensor.get_transfer_fee(wallet=wallet, dest=dest, value=amount) + fee = subtensor.get_transfer_fee( + wallet=wallet, dest=dest, value=amount, keep_alive=keep_alive + ) # Check if we have enough balance. if transfer_all is True: - amount = account_balance - fee - existential_deposit - if amount < Balance(0): + if (account_balance - fee) < existential_deposit: logging.error("Not enough balance to transfer") return False - - if account_balance < (amount + fee + existential_deposit): + elif account_balance < (amount + fee + existential_deposit): logging.error(":cross_mark: [red]Not enough balance[/red]") logging.error(f"\t\tBalance:\t[blue]{account_balance}[/blue]") logging.error(f"\t\tAmount:\t[blue]{amount}[/blue]") @@ -149,6 +158,7 @@ def transfer_extrinsic( wallet=wallet, destination=dest, amount=amount, + keep_alive=keep_alive, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, period=period, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index b28c49533f..d1b95521d8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -107,6 +107,7 @@ u16_normalized_float, u64_normalized_float, deprecated_message, + get_transfer_fn_params, ) from bittensor.utils.balance import ( Balance, @@ -2243,7 +2244,13 @@ def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]: ) return getattr(result, "value", None) - def get_transfer_fee(self, wallet: "Wallet", dest: str, value: Balance) -> Balance: + def get_transfer_fee( + self, + wallet: "Wallet", + dest: str, + value: Optional[Balance], + keep_alive: bool = True, + ) -> Balance: """ Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This function simulates the transfer to estimate the associated cost, taking into account the current network @@ -2254,6 +2261,8 @@ def get_transfer_fee(self, wallet: "Wallet", dest: str, value: Balance) -> Balan dest (str): The ``SS58`` address of the destination account. value (Union[bittensor.utils.balance.Balance, float, int]): The amount of tokens to be transferred, specified as a Balance object, or in Tao (float) or Rao (int) units. + keep_alive: Whether the transfer fee should be calculated based on keeping the wallet alive (existential + deposit) or not. Returns: bittensor.utils.balance.Balance: The estimated transaction fee for the transfer, represented as a Balance @@ -2263,11 +2272,15 @@ def get_transfer_fee(self, wallet: "Wallet", dest: str, value: Balance) -> Balan has sufficient funds to cover both the transfer amount and the associated costs. This function provides a crucial tool for managing financial operations within the Bittensor network. """ - value = check_and_convert_to_balance(value) + if value is not None: + value = check_and_convert_to_balance(value) + call_params: dict[str, Union[int, str, bool]] + call_function, call_params = get_transfer_fn_params(value, dest, keep_alive) + call = self.substrate.compose_call( call_module="Balances", - call_function="transfer_keep_alive", - call_params={"dest": dest, "value": value.rao}, + call_function=call_function, + call_params=call_params, ) try: @@ -4266,7 +4279,7 @@ def transfer( self, wallet: "Wallet", dest: str, - amount: Balance, + amount: Optional[Balance], wait_for_inclusion: bool = True, wait_for_finalization: bool = False, transfer_all: bool = False, @@ -4292,7 +4305,8 @@ def transfer( Returns: `True` if the transferring was successful, otherwise `False`. """ - amount = check_and_convert_to_balance(amount) + if amount is not None: + amount = check_and_convert_to_balance(amount) return transfer_extrinsic( subtensor=self, wallet=wallet, diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 26da8b5480..0d808477be 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -22,6 +22,7 @@ if TYPE_CHECKING: from bittensor_wallet import Wallet + from bittensor.utils.balance import Balance BT_DOCS_LINK = "https://docs.bittensor.com" @@ -445,3 +446,34 @@ def deprecated_message(message: str) -> None: """Shows a deprecation warning message with the given message.""" warnings.simplefilter("default", DeprecationWarning) warnings.warn(message=message, category=DeprecationWarning, stacklevel=2) + + +def get_transfer_fn_params( + amount: Optional["Balance"], destination: str, keep_alive: bool +) -> tuple[str, dict[str, Union[str, int, bool]]]: + """ + Helper function to get the transfer call function and call params, depending on the value and keep_alive flag + provided + + Args: + amount: the amount of Tao to transfer. `None` if transferring all. + destination: the destination SS58 of the transfer + keep_alive: whether to enforce a retention of the existential deposit in the account after transfer. + + Returns: + tuple[call function, call params] + """ + call_params = {"dest": destination} + if amount is None: + call_function = "transfer_all" + if keep_alive: + call_params["keep_alive"] = True + else: + call_params["keep_alive"] = False + else: + call_params["value"] = amount.rao + if keep_alive: + call_function = "transfer_keep_alive" + else: + call_function = "transfer_allow_death" + return call_function, call_params diff --git a/tests/e2e_tests/test_transfer.py b/tests/e2e_tests/test_transfer.py index 7a0728de72..0663b540b2 100644 --- a/tests/e2e_tests/test_transfer.py +++ b/tests/e2e_tests/test_transfer.py @@ -1,10 +1,18 @@ +import typing + +from bittensor_wallet import Wallet +import pytest + from bittensor.utils.balance import Balance from bittensor import logging +if typing.TYPE_CHECKING: + from bittensor.core.subtensor_api import SubtensorApi + logging.set_trace() -def test_transfer(subtensor, alice_wallet): +def test_transfer(subtensor: "SubtensorApi", alice_wallet): """ Test the transfer mechanism on the chain @@ -47,3 +55,90 @@ def test_transfer(subtensor, alice_wallet): ) print("✅ Passed test_transfer") + + +def test_transfer_all(subtensor: "Subtensor", alice_wallet): + # create two dummy accounts we can drain + dummy_account_1 = Wallet(path="/tmp/bittensor-dummy-account-1") + dummy_account_2 = Wallet(path="/tmp/bittensor-dummy-account-2") + dummy_account_1.create_new_coldkey(use_password=False, overwrite=True) + dummy_account_2.create_new_coldkey(use_password=False, overwrite=True) + + # fund the first dummy account + assert subtensor.transfer( + alice_wallet, + dest=dummy_account_1.coldkeypub.ss58_address, + amount=Balance.from_tao(2.0), + wait_for_finalization=True, + wait_for_inclusion=True, + ) + # Account details before transfer + existential_deposit = subtensor.get_existential_deposit() + assert subtensor.transfer( + wallet=dummy_account_1, + dest=dummy_account_2.coldkeypub.ss58_address, + amount=None, + transfer_all=True, + wait_for_finalization=True, + wait_for_inclusion=True, + keep_alive=True, + ) + balance_after = subtensor.get_balance(dummy_account_1.coldkeypub.ss58_address) + assert balance_after == existential_deposit + assert subtensor.transfer( + wallet=dummy_account_2, + dest=alice_wallet.coldkeypub.ss58_address, + amount=None, + transfer_all=True, + wait_for_inclusion=True, + wait_for_finalization=True, + keep_alive=False, + ) + balance_after = subtensor.get_balance(dummy_account_2.coldkeypub.ss58_address) + assert balance_after == Balance(0) + + +@pytest.mark.asyncio +async def test_async_transfer(async_subtensor: "SubtensorApi", alice_wallet): + # create two dummy accounts we can drain + dummy_account_1 = Wallet(path="/tmp/bittensor-dummy-account-3") + dummy_account_2 = Wallet(path="/tmp/bittensor-dummy-account-4") + dummy_account_1.create_new_coldkey(use_password=False, overwrite=True) + dummy_account_2.create_new_coldkey(use_password=False, overwrite=True) + + # fund the first dummy account + assert await async_subtensor.transfer( + alice_wallet, + dest=dummy_account_1.coldkeypub.ss58_address, + amount=Balance.from_tao(2.0), + wait_for_finalization=True, + wait_for_inclusion=True, + ) + # Account details before transfer + existential_deposit = await async_subtensor.get_existential_deposit() + assert await async_subtensor.transfer( + wallet=dummy_account_1, + dest=dummy_account_2.coldkeypub.ss58_address, + amount=None, + transfer_all=True, + wait_for_finalization=True, + wait_for_inclusion=True, + keep_alive=True, + ) + balance_after = await async_subtensor.get_balance( + dummy_account_1.coldkeypub.ss58_address + ) + assert balance_after == existential_deposit + assert await async_subtensor.transfer( + wallet=dummy_account_2, + dest=alice_wallet.coldkeypub.ss58_address, + amount=None, + transfer_all=True, + wait_for_inclusion=True, + wait_for_finalization=True, + keep_alive=False, + ) + balance_after = await async_subtensor.get_balance( + dummy_account_2.coldkeypub.ss58_address + ) + assert balance_after == Balance(0) diff --git a/tests/unit_tests/extrinsics/asyncex/test_transfer.py b/tests/unit_tests/extrinsics/asyncex/test_transfer.py index 95c5249b62..299c6df446 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_transfer.py +++ b/tests/unit_tests/extrinsics/asyncex/test_transfer.py @@ -3,12 +3,32 @@ from bittensor.utils.balance import Balance +@pytest.mark.parametrize( + "amount,keep_alive,call_function,call_params", + [ + ( + Balance(1), + True, + "transfer_keep_alive", + {"dest": "SS58PUBLICKEY", "value": Balance(1).rao}, + ), + (None, True, "transfer_all", {"dest": "SS58PUBLICKEY", "keep_alive": True}), + (None, False, "transfer_all", {"dest": "SS58PUBLICKEY", "keep_alive": False}), + ( + Balance(1), + False, + "transfer_allow_death", + {"dest": "SS58PUBLICKEY", "value": Balance(1).rao}, + ), + ], +) @pytest.mark.asyncio -async def test_do_transfer_success(subtensor, fake_wallet, mocker): +async def test_do_transfer_success( + subtensor, fake_wallet, mocker, amount, keep_alive, call_function, call_params +): """Tests _do_transfer when the transfer is successful.""" # Preps - fake_destination = "destination_address" - fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_destination = "SS58PUBLICKEY" fake_block_hash = "fake_block_hash" mocker.patch.object(subtensor.substrate, "compose_call") @@ -24,7 +44,8 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): subtensor=subtensor, wallet=fake_wallet, destination=fake_destination, - amount=fake_amount, + amount=amount, + keep_alive=keep_alive, wait_for_inclusion=True, wait_for_finalization=True, ) @@ -32,8 +53,8 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): # Asserts subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", - call_function="transfer_keep_alive", - call_params={"dest": fake_destination, "value": fake_amount.rao}, + call_function=call_function, + call_params=call_params, ) subtensor.sign_and_send_extrinsic.assert_awaited_once_with( diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index 081a56ffae..6aedaea601 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -1,12 +1,34 @@ from bittensor.core.extrinsics.transfer import _do_transfer from bittensor.utils.balance import Balance - -def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): +import pytest + + +@pytest.mark.parametrize( + "amount,keep_alive,call_function,call_params", + [ + ( + Balance(1), + True, + "transfer_keep_alive", + {"dest": "SS58PUBLICKEY", "value": Balance(1).rao}, + ), + (None, True, "transfer_all", {"dest": "SS58PUBLICKEY", "keep_alive": True}), + (None, False, "transfer_all", {"dest": "SS58PUBLICKEY", "keep_alive": False}), + ( + Balance(1), + False, + "transfer_allow_death", + {"dest": "SS58PUBLICKEY", "value": Balance(1).rao}, + ), + ], +) +def test_do_transfer_is_success_true( + subtensor, fake_wallet, mocker, amount, keep_alive, call_function, call_params +): """Successful do_transfer call.""" # Prep fake_dest = "SS58PUBLICKEY" - fake_transfer_balance = Balance(1) fake_wait_for_inclusion = True fake_wait_for_finalization = True @@ -18,16 +40,17 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): subtensor, fake_wallet, fake_dest, - fake_transfer_balance, + amount, fake_wait_for_inclusion, fake_wait_for_finalization, + keep_alive=keep_alive, ) # Asserts subtensor.substrate.compose_call.assert_called_once_with( call_module="Balances", - call_function="transfer_keep_alive", - call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, + call_function=call_function, + call_params=call_params, ) subtensor.sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, @@ -45,6 +68,7 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): # Prep fake_dest = "SS58PUBLICKEY" fake_transfer_balance = Balance(1) + keep_alive = True fake_wait_for_inclusion = True fake_wait_for_finalization = True @@ -59,6 +83,7 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): fake_transfer_balance, fake_wait_for_inclusion, fake_wait_for_finalization, + keep_alive=keep_alive, ) # Asserts @@ -83,6 +108,7 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): # Prep fake_dest = "SS58PUBLICKEY" fake_transfer_balance = Balance(1) + keep_alive = True fake_wait_for_inclusion = False fake_wait_for_finalization = False @@ -98,6 +124,7 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): fake_transfer_balance, fake_wait_for_inclusion, fake_wait_for_finalization, + keep_alive=keep_alive, ) # Asserts