meshtensor-cli 9.18.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. meshtensor_cli/__init__.py +22 -0
  2. meshtensor_cli/cli.py +10742 -0
  3. meshtensor_cli/doc_generation_helper.py +4 -0
  4. meshtensor_cli/src/__init__.py +1085 -0
  5. meshtensor_cli/src/commands/__init__.py +0 -0
  6. meshtensor_cli/src/commands/axon/__init__.py +0 -0
  7. meshtensor_cli/src/commands/axon/axon.py +132 -0
  8. meshtensor_cli/src/commands/crowd/__init__.py +0 -0
  9. meshtensor_cli/src/commands/crowd/contribute.py +621 -0
  10. meshtensor_cli/src/commands/crowd/contributors.py +200 -0
  11. meshtensor_cli/src/commands/crowd/create.py +783 -0
  12. meshtensor_cli/src/commands/crowd/dissolve.py +219 -0
  13. meshtensor_cli/src/commands/crowd/refund.py +233 -0
  14. meshtensor_cli/src/commands/crowd/update.py +418 -0
  15. meshtensor_cli/src/commands/crowd/utils.py +124 -0
  16. meshtensor_cli/src/commands/crowd/view.py +991 -0
  17. meshtensor_cli/src/commands/governance/__init__.py +0 -0
  18. meshtensor_cli/src/commands/governance/governance.py +794 -0
  19. meshtensor_cli/src/commands/liquidity/__init__.py +0 -0
  20. meshtensor_cli/src/commands/liquidity/liquidity.py +699 -0
  21. meshtensor_cli/src/commands/liquidity/utils.py +202 -0
  22. meshtensor_cli/src/commands/proxy.py +700 -0
  23. meshtensor_cli/src/commands/stake/__init__.py +0 -0
  24. meshtensor_cli/src/commands/stake/add.py +799 -0
  25. meshtensor_cli/src/commands/stake/auto_staking.py +306 -0
  26. meshtensor_cli/src/commands/stake/children_hotkeys.py +865 -0
  27. meshtensor_cli/src/commands/stake/claim.py +770 -0
  28. meshtensor_cli/src/commands/stake/list.py +738 -0
  29. meshtensor_cli/src/commands/stake/move.py +1211 -0
  30. meshtensor_cli/src/commands/stake/remove.py +1466 -0
  31. meshtensor_cli/src/commands/stake/wizard.py +323 -0
  32. meshtensor_cli/src/commands/subnets/__init__.py +0 -0
  33. meshtensor_cli/src/commands/subnets/mechanisms.py +515 -0
  34. meshtensor_cli/src/commands/subnets/price.py +733 -0
  35. meshtensor_cli/src/commands/subnets/subnets.py +2908 -0
  36. meshtensor_cli/src/commands/sudo.py +1294 -0
  37. meshtensor_cli/src/commands/tc/__init__.py +0 -0
  38. meshtensor_cli/src/commands/tc/tc.py +190 -0
  39. meshtensor_cli/src/commands/treasury/__init__.py +0 -0
  40. meshtensor_cli/src/commands/treasury/treasury.py +194 -0
  41. meshtensor_cli/src/commands/view.py +354 -0
  42. meshtensor_cli/src/commands/wallets.py +2311 -0
  43. meshtensor_cli/src/commands/weights.py +467 -0
  44. meshtensor_cli/src/meshtensor/__init__.py +0 -0
  45. meshtensor_cli/src/meshtensor/balances.py +313 -0
  46. meshtensor_cli/src/meshtensor/chain_data.py +1263 -0
  47. meshtensor_cli/src/meshtensor/extrinsics/__init__.py +0 -0
  48. meshtensor_cli/src/meshtensor/extrinsics/mev_shield.py +174 -0
  49. meshtensor_cli/src/meshtensor/extrinsics/registration.py +1861 -0
  50. meshtensor_cli/src/meshtensor/extrinsics/root.py +550 -0
  51. meshtensor_cli/src/meshtensor/extrinsics/serving.py +255 -0
  52. meshtensor_cli/src/meshtensor/extrinsics/transfer.py +239 -0
  53. meshtensor_cli/src/meshtensor/meshtensor_interface.py +2598 -0
  54. meshtensor_cli/src/meshtensor/minigraph.py +254 -0
  55. meshtensor_cli/src/meshtensor/networking.py +12 -0
  56. meshtensor_cli/src/meshtensor/templates/main-filters.j2 +24 -0
  57. meshtensor_cli/src/meshtensor/templates/main-header.j2 +36 -0
  58. meshtensor_cli/src/meshtensor/templates/neuron-details.j2 +111 -0
  59. meshtensor_cli/src/meshtensor/templates/price-multi.j2 +113 -0
  60. meshtensor_cli/src/meshtensor/templates/price-single.j2 +99 -0
  61. meshtensor_cli/src/meshtensor/templates/subnet-details-header.j2 +49 -0
  62. meshtensor_cli/src/meshtensor/templates/subnet-details.j2 +32 -0
  63. meshtensor_cli/src/meshtensor/templates/subnet-metrics.j2 +57 -0
  64. meshtensor_cli/src/meshtensor/templates/subnets-table.j2 +28 -0
  65. meshtensor_cli/src/meshtensor/templates/table.j2 +267 -0
  66. meshtensor_cli/src/meshtensor/templates/view.css +1058 -0
  67. meshtensor_cli/src/meshtensor/templates/view.j2 +43 -0
  68. meshtensor_cli/src/meshtensor/templates/view.js +1053 -0
  69. meshtensor_cli/src/meshtensor/utils.py +2007 -0
  70. meshtensor_cli/version.py +23 -0
  71. meshtensor_cli-9.18.1.dist-info/METADATA +261 -0
  72. meshtensor_cli-9.18.1.dist-info/RECORD +74 -0
  73. meshtensor_cli-9.18.1.dist-info/WHEEL +4 -0
  74. meshtensor_cli-9.18.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,255 @@
1
+ """
2
+ Extrinsics for serving operations (axon management).
3
+ """
4
+
5
+ import typing
6
+ from typing import Optional
7
+
8
+ from meshtensor_wallet import Wallet
9
+
10
+ from meshtensor_cli.src.meshtensor.utils import (
11
+ confirm_action,
12
+ console,
13
+ print_error,
14
+ print_success,
15
+ format_error_message,
16
+ unlock_key,
17
+ print_extrinsic_id,
18
+ )
19
+
20
+ if typing.TYPE_CHECKING:
21
+ from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
22
+
23
+
24
+ def ip_to_int(ip_str: str) -> int:
25
+ """
26
+ Converts an IP address string to its integer representation.
27
+
28
+ Args:
29
+ ip_str: IP address string (e.g., "192.168.1.1")
30
+
31
+ Returns:
32
+ Integer representation of the IP address
33
+ """
34
+ import netaddr
35
+
36
+ return int(netaddr.IPAddress(ip_str))
37
+
38
+
39
+ async def reset_axon_extrinsic(
40
+ meshtensor: "MeshtensorInterface",
41
+ wallet: Wallet,
42
+ netuid: int,
43
+ prompt: bool = False,
44
+ decline: bool = False,
45
+ quiet: bool = False,
46
+ wait_for_inclusion: bool = True,
47
+ wait_for_finalization: bool = False,
48
+ ) -> tuple[bool, str, Optional[str]]:
49
+ """
50
+ Resets the axon information for a neuron on the network.
51
+
52
+ This effectively removes the serving endpoint by setting the IP to 0.0.0.0
53
+ and port to 0, indicating the neuron is no longer serving.
54
+
55
+ Args:
56
+ meshtensor: The meshtensor interface to use for the extrinsic
57
+ wallet: The wallet containing the hotkey to reset the axon for
58
+ netuid: The network UID where the neuron is registered
59
+ prompt: Whether to prompt for confirmation before submitting
60
+ wait_for_inclusion: Whether to wait for the extrinsic to be included in a block
61
+ wait_for_finalization: Whether to wait for the extrinsic to be finalized
62
+
63
+ Returns:
64
+ Tuple of (success: bool, message: str, extrinsic_id: Optional[str])
65
+ """
66
+ # Unlock the hotkey
67
+ if not (
68
+ unlock_status := unlock_key(wallet, unlock_type="hot", print_out=False)
69
+ ).success:
70
+ return False, unlock_status.message, None
71
+
72
+ # Prompt for confirmation if requested
73
+ if prompt:
74
+ if not confirm_action(
75
+ f"Do you want to reset the axon for hotkey [bold]{wallet.hotkey.ss58_address}[/bold] "
76
+ f"on netuid [bold]{netuid}[/bold]?",
77
+ decline=decline,
78
+ quiet=quiet,
79
+ ):
80
+ return False, "User cancelled the operation", None
81
+
82
+ with console.status(
83
+ f":satellite: Resetting axon on [white]netuid {netuid}[/white]..."
84
+ ):
85
+ try:
86
+ # Compose the serve_axon call with reset values (IP: 0.0.0.0, port: 1)
87
+ # Note: Port must be >= 1 as chain rejects port 0 as invalid
88
+ call = await meshtensor.substrate.compose_call(
89
+ call_module="MeshtensorModule",
90
+ call_function="serve_axon",
91
+ call_params={
92
+ "netuid": netuid,
93
+ "version": 0,
94
+ "ip": ip_to_int("0.0.0.0"),
95
+ "port": 1,
96
+ "ip_type": 4, # IPv4
97
+ "protocol": 4,
98
+ "placeholder1": 0,
99
+ "placeholder2": 0,
100
+ },
101
+ )
102
+
103
+ # Sign with hotkey and submit the extrinsic
104
+ extrinsic = await meshtensor.substrate.create_signed_extrinsic(
105
+ call=call,
106
+ keypair=wallet.hotkey,
107
+ )
108
+ response = await meshtensor.substrate.submit_extrinsic(
109
+ extrinsic,
110
+ wait_for_inclusion=wait_for_inclusion,
111
+ wait_for_finalization=wait_for_finalization,
112
+ )
113
+
114
+ # We only wait here if we expect finalization.
115
+ if not wait_for_finalization and not wait_for_inclusion:
116
+ print_success(
117
+ "[dark_sea_green3]Axon reset successfully[/dark_sea_green3]"
118
+ )
119
+ return True, "Not waiting for finalization or inclusion.", None
120
+
121
+ success = await response.is_success
122
+ if not success:
123
+ error_msg = format_error_message(await response.error_message)
124
+ print_error(f"Failed: {error_msg}")
125
+ return False, error_msg, None
126
+ else:
127
+ ext_id = await response.get_extrinsic_identifier()
128
+ await print_extrinsic_id(response)
129
+ print_success(
130
+ "[dark_sea_green3]Axon reset successfully[/dark_sea_green3]"
131
+ )
132
+ return True, "Axon reset successfully", ext_id
133
+
134
+ except Exception as e:
135
+ error_message = format_error_message(e)
136
+ print_error(f"Failed to reset axon: {error_message}")
137
+ return False, error_message, None
138
+
139
+
140
+ async def set_axon_extrinsic(
141
+ meshtensor: "MeshtensorInterface",
142
+ wallet: Wallet,
143
+ netuid: int,
144
+ ip: str,
145
+ port: int,
146
+ ip_type: int = 4,
147
+ protocol: int = 4,
148
+ prompt: bool = False,
149
+ decline: bool = False,
150
+ quiet: bool = False,
151
+ wait_for_inclusion: bool = True,
152
+ wait_for_finalization: bool = False,
153
+ ) -> tuple[bool, str, Optional[str]]:
154
+ """
155
+ Sets the axon information for a neuron on the network.
156
+
157
+ This configures the serving endpoint for a neuron by specifying its IP address
158
+ and port, allowing other neurons to connect to it.
159
+
160
+ Args:
161
+ meshtensor: The meshtensor interface to use for the extrinsic
162
+ wallet: The wallet containing the hotkey to set the axon for
163
+ netuid: The network UID where the neuron is registered
164
+ ip: The IP address to set (e.g., "192.168.1.1")
165
+ port: The port number to set
166
+ ip_type: IP type (4 for IPv4, 6 for IPv6)
167
+ protocol: Protocol version (default: 4)
168
+ prompt: Whether to prompt for confirmation before submitting
169
+ wait_for_inclusion: Whether to wait for the extrinsic to be included in a block
170
+ wait_for_finalization: Whether to wait for the extrinsic to be finalized
171
+
172
+ Returns:
173
+ Tuple of (success: bool, message: str, extrinsic_id: Optional[str])
174
+ """
175
+ # Validate port
176
+ if not (0 <= port <= 65535):
177
+ return False, f"Invalid port number: {port}. Must be between 0 and 65535.", None
178
+
179
+ # Validate IP address
180
+ try:
181
+ ip_int = ip_to_int(ip)
182
+ except Exception as e:
183
+ return False, f"Invalid IP address: {ip}. Error: {str(e)}", None
184
+
185
+ # Unlock the hotkey
186
+ if not (
187
+ unlock_status := unlock_key(wallet, unlock_type="hot", print_out=False)
188
+ ).success:
189
+ return False, unlock_status.message, None
190
+
191
+ # Prompt for confirmation if requested
192
+ if prompt:
193
+ if not confirm_action(
194
+ f"Do you want to set the axon for hotkey [bold]{wallet.hotkey.ss58_address}[/bold] "
195
+ f"on netuid [bold]{netuid}[/bold] to [bold]{ip}:{port}[/bold]?",
196
+ decline=decline,
197
+ quiet=quiet,
198
+ ):
199
+ return False, "User cancelled the operation", None
200
+
201
+ with console.status(
202
+ f":satellite: Setting axon on [white]netuid {netuid}[/white] to [white]{ip}:{port}[/white]..."
203
+ ):
204
+ try:
205
+ # Compose the serve_axon call
206
+ call = await meshtensor.substrate.compose_call(
207
+ call_module="MeshtensorModule",
208
+ call_function="serve_axon",
209
+ call_params={
210
+ "netuid": netuid,
211
+ "version": 0,
212
+ "ip": ip_int,
213
+ "port": port,
214
+ "ip_type": ip_type,
215
+ "protocol": protocol,
216
+ "placeholder1": 0,
217
+ "placeholder2": 0,
218
+ },
219
+ )
220
+
221
+ # Sign with hotkey and submit the extrinsic
222
+ extrinsic = await meshtensor.substrate.create_signed_extrinsic(
223
+ call=call,
224
+ keypair=wallet.hotkey,
225
+ )
226
+ response = await meshtensor.substrate.submit_extrinsic(
227
+ extrinsic,
228
+ wait_for_inclusion=wait_for_inclusion,
229
+ wait_for_finalization=wait_for_finalization,
230
+ )
231
+
232
+ # We only wait here if we expect finalization.
233
+ if not wait_for_finalization and not wait_for_inclusion:
234
+ print_success(
235
+ f"[dark_sea_green3]Axon set successfully to {ip}:{port}[/dark_sea_green3]"
236
+ )
237
+ return True, "Not waiting for finalization or inclusion.", None
238
+
239
+ success = await response.is_success
240
+ if not success:
241
+ error_msg = format_error_message(await response.error_message)
242
+ print_error(f"Failed: {error_msg}")
243
+ return False, error_msg, None
244
+ else:
245
+ ext_id = await response.get_extrinsic_identifier()
246
+ await print_extrinsic_id(response)
247
+ print_success(
248
+ f"[dark_sea_green3]Axon set successfully to {ip}:{port}[/dark_sea_green3]"
249
+ )
250
+ return True, f"Axon set successfully to {ip}:{port}", ext_id
251
+
252
+ except Exception as e:
253
+ error_message = format_error_message(e)
254
+ print_error(f"Failed to set axon: {error_message}")
255
+ return False, error_message, None
@@ -0,0 +1,239 @@
1
+ import asyncio
2
+ from typing import Optional, Union
3
+
4
+ from async_substrate_interface import AsyncExtrinsicReceipt
5
+ from meshtensor_wallet import Wallet
6
+ from meshtensor_cli.src.meshtensor.balances import Balance
7
+ from meshtensor_cli.src.meshtensor.meshtensor_interface import (
8
+ MeshtensorInterface,
9
+ GENESIS_ADDRESS,
10
+ )
11
+ from meshtensor_cli.src.meshtensor.utils import (
12
+ confirm_action,
13
+ console,
14
+ print_error,
15
+ print_success,
16
+ print_verbose,
17
+ is_valid_meshtensor_address_or_public_key,
18
+ print_error,
19
+ unlock_key,
20
+ )
21
+
22
+
23
+ async def transfer_extrinsic(
24
+ meshtensor: MeshtensorInterface,
25
+ wallet: Wallet,
26
+ destination: str,
27
+ amount: Balance,
28
+ era: int = 3,
29
+ transfer_all: bool = False,
30
+ allow_death: bool = False,
31
+ wait_for_inclusion: bool = True,
32
+ wait_for_finalization: bool = False,
33
+ prompt: bool = False,
34
+ decline: bool = False,
35
+ quiet: bool = False,
36
+ proxy: Optional[str] = None,
37
+ announce_only: bool = False,
38
+ ) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
39
+ """Transfers funds from this wallet to the destination public key address.
40
+
41
+ :param meshtensor: initialized MeshtensorInterface object used for transfer
42
+ :param wallet: Meshtensor wallet object to make transfer from.
43
+ :param destination: Destination public key address (ss58_address or ed25519) of recipient.
44
+ :param amount: Amount to stake as Meshtensor balance.
45
+ :param era: Length (in blocks) for which the transaction should be valid.
46
+ :param transfer_all: Whether to transfer all funds from this wallet to the destination address.
47
+ :param allow_death: Whether to allow for falling below the existential deposit when performing this transfer.
48
+ :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`,
49
+ or returns `False` if the extrinsic fails to enter the block within the timeout.
50
+ :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning
51
+ `True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
52
+ :param prompt: If `True`, the call waits for confirmation from the user before proceeding.
53
+ :param proxy: Optional proxy to use for this call.
54
+ :param announce_only: If set along with proxy, will make this call as an announcement, rather than making the call
55
+
56
+ :return: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for
57
+ finalization / inclusion, the response is `True`, regardless of its inclusion.
58
+ """
59
+
60
+ async def get_transfer_fee() -> Balance:
61
+ """
62
+ Calculates the transaction fee for transferring tokens from a wallet to a specified destination address.
63
+ This function simulates the transfer to estimate the associated cost, taking into account the current
64
+ network conditions and transaction complexity.
65
+ """
66
+ call = await meshtensor.substrate.compose_call(
67
+ call_module="Balances",
68
+ call_function=call_function,
69
+ call_params=call_params,
70
+ )
71
+ return await meshtensor.get_extrinsic_fee(
72
+ call=call, keypair=wallet.coldkeypub, proxy=proxy
73
+ )
74
+
75
+ async def do_transfer() -> tuple[bool, str, str, Optional[AsyncExtrinsicReceipt]]:
76
+ """
77
+ Makes transfer from wallet to destination public key address.
78
+ :return: success, block hash, formatted error message
79
+ """
80
+ call = await meshtensor.substrate.compose_call(
81
+ call_module="Balances",
82
+ call_function=call_function,
83
+ call_params=call_params,
84
+ )
85
+ success_, error_msg_, receipt_ = await meshtensor.sign_and_send_extrinsic(
86
+ call=call,
87
+ wallet=wallet,
88
+ wait_for_finalization=wait_for_finalization,
89
+ wait_for_inclusion=wait_for_inclusion,
90
+ proxy=proxy,
91
+ era={"period": era},
92
+ announce_only=announce_only,
93
+ )
94
+ block_hash_ = receipt_.block_hash if receipt_ is not None else ""
95
+ return success_, block_hash_, error_msg_, receipt_
96
+
97
+ # Validate destination address.
98
+ if not is_valid_meshtensor_address_or_public_key(destination):
99
+ print_error(
100
+ f"Invalid destination SS58 address:[bold white]\n {destination}[/bold white]"
101
+ )
102
+ return False, None
103
+ console.print(f"[dark_orange]Initiating transfer on network: {meshtensor.network}")
104
+
105
+ call_params: dict[str, Optional[Union[str, int]]] = {"dest": destination}
106
+ if transfer_all:
107
+ call_function = "transfer_all"
108
+ if allow_death:
109
+ call_params["keep_alive"] = False
110
+ else:
111
+ call_params["keep_alive"] = True
112
+ else:
113
+ call_params["value"] = amount.meshlet
114
+ if allow_death:
115
+ call_function = "transfer_allow_death"
116
+ else:
117
+ call_function = "transfer_keep_alive"
118
+
119
+ # Check balance.
120
+ with console.status(
121
+ f":satellite: Checking balance and fees on chain [white]{meshtensor.network}[/white]",
122
+ spinner="aesthetic",
123
+ ) as status:
124
+ # check existential deposit and fee
125
+ print_verbose("Fetching existential and fee", status)
126
+ block_hash = await meshtensor.substrate.get_chain_head()
127
+ if proxy:
128
+ proxy_balance = await meshtensor.get_balance(proxy, block_hash=block_hash)
129
+ account_balance, existential_deposit, fee = await asyncio.gather(
130
+ meshtensor.get_balance(
131
+ wallet.coldkeypub.ss58_address, block_hash=block_hash
132
+ ),
133
+ meshtensor.get_existential_deposit(block_hash=block_hash),
134
+ get_transfer_fee(),
135
+ )
136
+
137
+ if allow_death:
138
+ # Check if the transfer should keep alive the account
139
+ existential_deposit = Balance(0)
140
+
141
+ if proxy:
142
+ if proxy_balance < (amount + existential_deposit) and not allow_death:
143
+ print_error(
144
+ "[bold red]Not enough balance[/bold red]:\n\n"
145
+ f" balance: [bright_cyan]{proxy_balance}[/bright_cyan]\n"
146
+ f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
147
+ f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
148
+ f"You can try again with `--allow-death`."
149
+ )
150
+ return False, None
151
+ if account_balance < fee:
152
+ print_error(
153
+ "[bold red]Not enough balance[/bold red]:\n\n"
154
+ f" balance: [bright_cyan]{account_balance}[/bright_cyan]\n"
155
+ f" fee: [bright_cyan]{fee}[/bright_cyan]\n"
156
+ f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
157
+ )
158
+ return False, None
159
+ if account_balance < amount and allow_death:
160
+ print_error(
161
+ "[bold red]Not enough balance[/bold red]:\n\n"
162
+ f" balance: [bright_red]{account_balance}[/bright_red]\n"
163
+ f" amount: [bright_red]{amount}[/bright_red]\n"
164
+ )
165
+ return False, None
166
+ else:
167
+ if account_balance < (amount + fee + existential_deposit) and not allow_death:
168
+ print_error(
169
+ "[bold red]Not enough balance[/bold red]:\n\n"
170
+ f" balance: [bright_cyan]{account_balance}[/bright_cyan]\n"
171
+ f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
172
+ f" for fee: [bright_cyan]{fee}[/bright_cyan]\n"
173
+ f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
174
+ f"You can try again with `--allow-death`."
175
+ )
176
+ return False, None
177
+ elif account_balance < (amount + fee) and allow_death:
178
+ print_error(
179
+ "[bold red]Not enough balance[/bold red]:\n\n"
180
+ f" balance: [bright_red]{account_balance}[/bright_red]\n"
181
+ f" amount: [bright_red]{amount}[/bright_red]\n"
182
+ f" for fee: [bright_red]{fee}[/bright_red]"
183
+ )
184
+ return False, None
185
+ if proxy:
186
+ account_balance = proxy_balance
187
+
188
+ # Ask before moving on.
189
+ if prompt:
190
+ hk_owner = await meshtensor.get_hotkey_owner(destination, check_exists=False)
191
+ if hk_owner and hk_owner not in (destination, GENESIS_ADDRESS):
192
+ if not confirm_action(
193
+ f"The destination appears to be a hotkey, owned by [bright_magenta]{hk_owner}[/bright_magenta]. "
194
+ f"Only proceed if you are absolutely sure that [bright_magenta]{destination}[/bright_magenta] is the "
195
+ f"correct destination.",
196
+ default=False,
197
+ decline=decline,
198
+ quiet=quiet,
199
+ ):
200
+ return False, None
201
+ if not confirm_action(
202
+ "Do you want to transfer:[bold white]\n"
203
+ f" amount: [bright_cyan]{amount if not transfer_all else account_balance}[/bright_cyan]\n"
204
+ f" from: [light_goldenrod2]{wallet.name}[/light_goldenrod2] : "
205
+ f"[bright_magenta]{wallet.coldkeypub.ss58_address}\n[/bright_magenta]"
206
+ f" to: [bright_magenta]{destination}[/bright_magenta]\n for fee: [bright_cyan]{fee}[/bright_cyan]\n"
207
+ f"[bright_yellow]Transferring is not the same as staking. To instead stake, use "
208
+ f"[dark_orange]meshcli stake add[/dark_orange] instead[/bright_yellow].\n"
209
+ f"Proceed with transfer?",
210
+ decline=decline,
211
+ quiet=quiet,
212
+ ):
213
+ return False, None
214
+
215
+ # Unlock wallet coldkey.
216
+ if not unlock_key(wallet).success:
217
+ return False, None
218
+
219
+ with console.status(":satellite: Transferring...", spinner="earth"):
220
+ success, block_hash, err_msg, ext_receipt = await do_transfer()
221
+
222
+ if success:
223
+ print_success(f"Finalized. Block Hash: {block_hash}")
224
+
225
+ else:
226
+ print_error(f"Failed: {err_msg}")
227
+
228
+ if success:
229
+ with console.status(":satellite: Checking Balance...", spinner="aesthetic"):
230
+ new_balance = await meshtensor.get_balance(
231
+ proxy or wallet.coldkeypub.ss58_address, reuse_block=False
232
+ )
233
+ console.print(
234
+ f"Balance:\n"
235
+ f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]"
236
+ )
237
+ return True, ext_receipt
238
+
239
+ return False, None