Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async unittests for bittensor/core/extrinsics/async_transfer.py #2426

Merged
merged 2 commits into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 71 additions & 79 deletions bittensor/core/extrinsics/async_transfer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import asyncio
from typing import TYPE_CHECKING

from bittensor_wallet import Wallet
from substrateinterface.exceptions import SubstrateRequestException

from bittensor.core.settings import NETWORK_EXPLORER_MAP
from bittensor.utils import (
format_error_message,
Expand All @@ -16,11 +13,66 @@

if TYPE_CHECKING:
from bittensor.core.async_subtensor import AsyncSubtensor
from bittensor_wallet import Wallet


async def _do_transfer(
subtensor: "AsyncSubtensor",
wallet: "Wallet",
destination: str,
amount: "Balance",
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
) -> tuple[bool, str, str]:
"""
Makes transfer from wallet to destination public key address.

Args:
subtensor (bittensor.core.async_subtensor.AsyncSubtensor): initialized AsyncSubtensor 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.
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 `True`, or returns `False` if the extrinsic fails to be finalized within the timeout.

Returns:
success, block hash, formatted error message
"""
call = await subtensor.substrate.compose_call(
call_module="Balances",
call_function="transfer_allow_death",
call_params={"dest": destination, "value": amount.rao},
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call, keypair=wallet.coldkey
)
response = await subtensor.substrate.submit_extrinsic(
extrinsic,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)
# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
return True, "", "Success, extrinsic submitted without waiting."

# Otherwise continue with finalization.
await response.process_events()
if await response.is_success:
block_hash_ = response.block_hash
return True, block_hash_, "Success with response."
else:
return (
False,
"",
format_error_message(
await response.error_message, substrate=subtensor.substrate
),
)


async def transfer_extrinsic(
subtensor: "AsyncSubtensor",
wallet: Wallet,
wallet: "Wallet",
destination: str,
amount: "Balance",
transfer_all: bool = False,
Expand All @@ -43,71 +95,6 @@ async def transfer_extrinsic(
Returns:
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.
"""

async def get_transfer_fee() -> 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 conditions and transaction complexity.
"""
call = await subtensor.substrate.compose_call(
call_module="Balances",
call_function="transfer_allow_death",
call_params={"dest": destination, "value": amount.rao},
)

try:
payment_info = await subtensor.substrate.get_payment_info(
call=call, keypair=wallet.coldkeypub
)
except SubstrateRequestException as e:
payment_info = {"partialFee": int(2e7)} # assume 0.02 Tao
logging.error(f":cross_mark: <red>Failed to get payment info</red>:")
logging.error(f"\t\t{format_error_message(e, subtensor.substrate)}")
logging.error(
f"\t\tDefaulting to default transfer fee: {payment_info['partialFee']}"
)

return Balance.from_rao(payment_info["partialFee"])

async def do_transfer() -> tuple[bool, str, str]:
"""
Makes transfer from wallet to destination public key address.

Returns:
success, block hash, formatted error message
"""
call = await subtensor.substrate.compose_call(
call_module="Balances",
call_function="transfer_allow_death",
call_params={"dest": destination, "value": amount.rao},
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call, keypair=wallet.coldkey
)
response = await subtensor.substrate.submit_extrinsic(
extrinsic,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)
# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
return True, "", ""

# Otherwise continue with finalization.
await response.process_events()
if await response.is_success:
block_hash_ = response.block_hash
return True, block_hash_, ""
else:
return (
False,
"",
format_error_message(
await response.error_message, substrate=subtensor.substrate
),
)

# Validate destination address.
if not is_valid_bittensor_address_or_public_key(destination):
logging.error(
Expand All @@ -132,7 +119,9 @@ async def do_transfer() -> tuple[bool, str, str]:
subtensor.get_existential_deposit(block_hash=block_hash),
)
account_balance = account_balance_[wallet.coldkeypub.ss58_address]
fee = await get_transfer_fee()
fee = await subtensor.get_transfer_fee(
wallet=wallet, dest=destination, value=amount.rao
)

if not keep_alive:
# Check if the transfer should keep_alive the account
Expand All @@ -153,7 +142,14 @@ async def do_transfer() -> tuple[bool, str, str]:
return False

logging.info(":satellite: <magenta>Transferring...</magenta")
success, block_hash, err_msg = await do_transfer()
success, block_hash, err_msg = await _do_transfer(
subtensor=subtensor,
wallet=wallet,
destination=destination,
amount=amount,
wait_for_finalization=wait_for_finalization,
wait_for_inclusion=wait_for_inclusion,
)

if success:
logging.success(":white_heavy_check_mark: [green]Finalized</green>")
Expand All @@ -171,17 +167,13 @@ async def do_transfer() -> tuple[bool, str, str]:
logging.info(
f"[green]Taostats Explorer Link: {explorer_urls.get('taostats')}</green>"
)
else:
logging.error(f":cross_mark: <red>Failed</red>: {err_msg}")

if success:
logging.info(":satellite: <magenta>Checking Balance...<magenta>")
new_balance = await subtensor.get_balance(
wallet.coldkeypub.ss58_address, reuse_block=False
)
new_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
logging.info(
f"Balance: [blue]{account_balance}</blue> :arrow_right: [green]{new_balance[wallet.coldkeypub.ss58_address]}</green>"
)
return True

return False
else:
logging.error(f":cross_mark: <red>Failed</red>: {err_msg}")
return False
Loading
Loading