Skip to content

Commit

Permalink
transfer_extrinsic
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-opentensor committed Nov 16, 2024
1 parent 7d4e633 commit 0e35bcb
Show file tree
Hide file tree
Showing 2 changed files with 574 additions and 79 deletions.
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

0 comments on commit 0e35bcb

Please sign in to comment.