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.
- meshtensor_cli/__init__.py +22 -0
- meshtensor_cli/cli.py +10742 -0
- meshtensor_cli/doc_generation_helper.py +4 -0
- meshtensor_cli/src/__init__.py +1085 -0
- meshtensor_cli/src/commands/__init__.py +0 -0
- meshtensor_cli/src/commands/axon/__init__.py +0 -0
- meshtensor_cli/src/commands/axon/axon.py +132 -0
- meshtensor_cli/src/commands/crowd/__init__.py +0 -0
- meshtensor_cli/src/commands/crowd/contribute.py +621 -0
- meshtensor_cli/src/commands/crowd/contributors.py +200 -0
- meshtensor_cli/src/commands/crowd/create.py +783 -0
- meshtensor_cli/src/commands/crowd/dissolve.py +219 -0
- meshtensor_cli/src/commands/crowd/refund.py +233 -0
- meshtensor_cli/src/commands/crowd/update.py +418 -0
- meshtensor_cli/src/commands/crowd/utils.py +124 -0
- meshtensor_cli/src/commands/crowd/view.py +991 -0
- meshtensor_cli/src/commands/governance/__init__.py +0 -0
- meshtensor_cli/src/commands/governance/governance.py +794 -0
- meshtensor_cli/src/commands/liquidity/__init__.py +0 -0
- meshtensor_cli/src/commands/liquidity/liquidity.py +699 -0
- meshtensor_cli/src/commands/liquidity/utils.py +202 -0
- meshtensor_cli/src/commands/proxy.py +700 -0
- meshtensor_cli/src/commands/stake/__init__.py +0 -0
- meshtensor_cli/src/commands/stake/add.py +799 -0
- meshtensor_cli/src/commands/stake/auto_staking.py +306 -0
- meshtensor_cli/src/commands/stake/children_hotkeys.py +865 -0
- meshtensor_cli/src/commands/stake/claim.py +770 -0
- meshtensor_cli/src/commands/stake/list.py +738 -0
- meshtensor_cli/src/commands/stake/move.py +1211 -0
- meshtensor_cli/src/commands/stake/remove.py +1466 -0
- meshtensor_cli/src/commands/stake/wizard.py +323 -0
- meshtensor_cli/src/commands/subnets/__init__.py +0 -0
- meshtensor_cli/src/commands/subnets/mechanisms.py +515 -0
- meshtensor_cli/src/commands/subnets/price.py +733 -0
- meshtensor_cli/src/commands/subnets/subnets.py +2908 -0
- meshtensor_cli/src/commands/sudo.py +1294 -0
- meshtensor_cli/src/commands/tc/__init__.py +0 -0
- meshtensor_cli/src/commands/tc/tc.py +190 -0
- meshtensor_cli/src/commands/treasury/__init__.py +0 -0
- meshtensor_cli/src/commands/treasury/treasury.py +194 -0
- meshtensor_cli/src/commands/view.py +354 -0
- meshtensor_cli/src/commands/wallets.py +2311 -0
- meshtensor_cli/src/commands/weights.py +467 -0
- meshtensor_cli/src/meshtensor/__init__.py +0 -0
- meshtensor_cli/src/meshtensor/balances.py +313 -0
- meshtensor_cli/src/meshtensor/chain_data.py +1263 -0
- meshtensor_cli/src/meshtensor/extrinsics/__init__.py +0 -0
- meshtensor_cli/src/meshtensor/extrinsics/mev_shield.py +174 -0
- meshtensor_cli/src/meshtensor/extrinsics/registration.py +1861 -0
- meshtensor_cli/src/meshtensor/extrinsics/root.py +550 -0
- meshtensor_cli/src/meshtensor/extrinsics/serving.py +255 -0
- meshtensor_cli/src/meshtensor/extrinsics/transfer.py +239 -0
- meshtensor_cli/src/meshtensor/meshtensor_interface.py +2598 -0
- meshtensor_cli/src/meshtensor/minigraph.py +254 -0
- meshtensor_cli/src/meshtensor/networking.py +12 -0
- meshtensor_cli/src/meshtensor/templates/main-filters.j2 +24 -0
- meshtensor_cli/src/meshtensor/templates/main-header.j2 +36 -0
- meshtensor_cli/src/meshtensor/templates/neuron-details.j2 +111 -0
- meshtensor_cli/src/meshtensor/templates/price-multi.j2 +113 -0
- meshtensor_cli/src/meshtensor/templates/price-single.j2 +99 -0
- meshtensor_cli/src/meshtensor/templates/subnet-details-header.j2 +49 -0
- meshtensor_cli/src/meshtensor/templates/subnet-details.j2 +32 -0
- meshtensor_cli/src/meshtensor/templates/subnet-metrics.j2 +57 -0
- meshtensor_cli/src/meshtensor/templates/subnets-table.j2 +28 -0
- meshtensor_cli/src/meshtensor/templates/table.j2 +267 -0
- meshtensor_cli/src/meshtensor/templates/view.css +1058 -0
- meshtensor_cli/src/meshtensor/templates/view.j2 +43 -0
- meshtensor_cli/src/meshtensor/templates/view.js +1053 -0
- meshtensor_cli/src/meshtensor/utils.py +2007 -0
- meshtensor_cli/version.py +23 -0
- meshtensor_cli-9.18.1.dist-info/METADATA +261 -0
- meshtensor_cli-9.18.1.dist-info/RECORD +74 -0
- meshtensor_cli-9.18.1.dist-info/WHEEL +4 -0
- 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
|