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,1211 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from meshtensor_wallet import Wallet
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.prompt import Prompt
|
|
9
|
+
|
|
10
|
+
from meshtensor_cli.src import COLOR_PALETTE
|
|
11
|
+
from meshtensor_cli.src.meshtensor.balances import Balance
|
|
12
|
+
from meshtensor_cli.src.meshtensor.extrinsics.mev_shield import (
|
|
13
|
+
extract_mev_shield_id,
|
|
14
|
+
wait_for_extrinsic_by_hash,
|
|
15
|
+
)
|
|
16
|
+
from meshtensor_cli.src.meshtensor.utils import (
|
|
17
|
+
confirm_action,
|
|
18
|
+
console,
|
|
19
|
+
print_error,
|
|
20
|
+
group_subnets,
|
|
21
|
+
get_subnet_name,
|
|
22
|
+
print_success,
|
|
23
|
+
unlock_key,
|
|
24
|
+
get_hotkey_pub_ss58,
|
|
25
|
+
print_extrinsic_id,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
|
|
30
|
+
from meshtensor_cli.src.meshtensor.chain_data import DynamicInfo
|
|
31
|
+
|
|
32
|
+
MIN_STAKE_FEE = Balance.from_meshlet(50_000)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Helpers
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class MovementPricing:
|
|
38
|
+
origin_subnet: "DynamicInfo"
|
|
39
|
+
destination_subnet: "DynamicInfo"
|
|
40
|
+
rate: float
|
|
41
|
+
rate_with_tolerance: Optional[float]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def get_movement_pricing(
|
|
45
|
+
meshtensor: "MeshtensorInterface",
|
|
46
|
+
origin_netuid: int,
|
|
47
|
+
destination_netuid: int,
|
|
48
|
+
safe_staking: bool = False,
|
|
49
|
+
rate_tolerance: Optional[float] = None,
|
|
50
|
+
) -> MovementPricing:
|
|
51
|
+
"""
|
|
52
|
+
Returns pricing information for stake movement commands based on the origin and destination subnets.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
meshtensor: MeshtensorInterface instance.
|
|
56
|
+
origin_netuid: The netuid of the origin subnet.
|
|
57
|
+
destination_netuid: The netuid of the destination subnet.
|
|
58
|
+
safe_staking: Whether to enable safe staking with slippage protection.
|
|
59
|
+
rate_tolerance: The accepted rate tolerance (slippage) for safe staking.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
MovementPricing: Object containing pricing details like rates and limits.
|
|
63
|
+
"""
|
|
64
|
+
if origin_netuid == destination_netuid:
|
|
65
|
+
subnet = await meshtensor.subnet(origin_netuid)
|
|
66
|
+
return MovementPricing(
|
|
67
|
+
origin_subnet=subnet,
|
|
68
|
+
destination_subnet=subnet,
|
|
69
|
+
rate=1.0,
|
|
70
|
+
rate_with_tolerance=1.0 if safe_staking else None,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
origin_subnet, destination_subnet = await asyncio.gather(
|
|
74
|
+
meshtensor.subnet(origin_netuid),
|
|
75
|
+
meshtensor.subnet(destination_netuid),
|
|
76
|
+
)
|
|
77
|
+
price_origin = origin_subnet.price.tao
|
|
78
|
+
price_destination = destination_subnet.price.tao
|
|
79
|
+
rate = price_origin / (price_destination or 1)
|
|
80
|
+
rate_with_tolerance = None
|
|
81
|
+
if safe_staking:
|
|
82
|
+
limit_rate = rate * (1 - rate_tolerance)
|
|
83
|
+
rate_with_tolerance = limit_rate
|
|
84
|
+
|
|
85
|
+
return MovementPricing(
|
|
86
|
+
origin_subnet=origin_subnet,
|
|
87
|
+
destination_subnet=destination_subnet,
|
|
88
|
+
rate=rate,
|
|
89
|
+
rate_with_tolerance=rate_with_tolerance,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def display_stake_movement_cross_subnets(
|
|
94
|
+
meshtensor: "MeshtensorInterface",
|
|
95
|
+
origin_netuid: int,
|
|
96
|
+
destination_netuid: int,
|
|
97
|
+
origin_hotkey: str,
|
|
98
|
+
destination_hotkey: str,
|
|
99
|
+
amount_to_move: Balance,
|
|
100
|
+
pricing: MovementPricing,
|
|
101
|
+
stake_fee: Balance,
|
|
102
|
+
extrinsic_fee: Balance,
|
|
103
|
+
safe_staking: bool = False,
|
|
104
|
+
rate_tolerance: Optional[float] = None,
|
|
105
|
+
allow_partial_stake: bool = False,
|
|
106
|
+
proxy: Optional[str] = None,
|
|
107
|
+
) -> tuple[Balance, str]:
|
|
108
|
+
"""Calculate and display stake movement information.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
meshtensor: MeshtensorInterface instance.
|
|
112
|
+
origin_netuid: The netuid of the origin subnet.
|
|
113
|
+
destination_netuid: The netuid of the destination subnet.
|
|
114
|
+
origin_hotkey: The origin hotkey SS58 address.
|
|
115
|
+
destination_hotkey: The destination hotkey SS58 address.
|
|
116
|
+
amount_to_move: The amount of stake to move/swap.
|
|
117
|
+
pricing: Pricing information including rates and limits.
|
|
118
|
+
stake_fee: The fee for the stake transaction.
|
|
119
|
+
extrinsic_fee: The fee for the extrinsic execution.
|
|
120
|
+
safe_staking: Whether to enable safe staking.
|
|
121
|
+
rate_tolerance: The accepted rate tolerance.
|
|
122
|
+
allow_partial_stake: Whether to allow partial execution if the full amount cannot be staked within limits.
|
|
123
|
+
proxy: Optional proxy address.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
tuple[Balance, str]: The estimated amount received and the formatted price string.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
if origin_netuid == destination_netuid:
|
|
130
|
+
subnet = pricing.origin_subnet
|
|
131
|
+
received_amount_tao = subnet.alpha_to_tao(amount_to_move - stake_fee)
|
|
132
|
+
if not proxy:
|
|
133
|
+
received_amount_tao -= extrinsic_fee
|
|
134
|
+
received_amount = subnet.tao_to_alpha(received_amount_tao)
|
|
135
|
+
|
|
136
|
+
if received_amount < Balance.from_tao(0).set_unit(destination_netuid):
|
|
137
|
+
print_error(
|
|
138
|
+
f"Not enough Alpha to pay the transaction fee. The fee is {stake_fee}, "
|
|
139
|
+
f"which would set the total received to {received_amount}."
|
|
140
|
+
)
|
|
141
|
+
raise ValueError
|
|
142
|
+
|
|
143
|
+
price = subnet.price.tao
|
|
144
|
+
price_str = (
|
|
145
|
+
str(float(price))
|
|
146
|
+
+ f"({Balance.get_unit(0)}/{Balance.get_unit(origin_netuid)})"
|
|
147
|
+
)
|
|
148
|
+
else:
|
|
149
|
+
dynamic_origin = pricing.origin_subnet
|
|
150
|
+
dynamic_destination = pricing.destination_subnet
|
|
151
|
+
received_amount_tao = (
|
|
152
|
+
dynamic_origin.alpha_to_tao(amount_to_move - stake_fee) - extrinsic_fee
|
|
153
|
+
)
|
|
154
|
+
received_amount = dynamic_destination.tao_to_alpha(received_amount_tao)
|
|
155
|
+
received_amount.set_unit(destination_netuid)
|
|
156
|
+
|
|
157
|
+
if received_amount < Balance.from_tao(0).set_unit(destination_netuid):
|
|
158
|
+
print_error(
|
|
159
|
+
f"Not enough Alpha to pay the transaction fee. The fee is {stake_fee}, "
|
|
160
|
+
f"which would set the total received to {received_amount}."
|
|
161
|
+
)
|
|
162
|
+
raise ValueError
|
|
163
|
+
|
|
164
|
+
price_str = (
|
|
165
|
+
f"{pricing.rate:.5f}"
|
|
166
|
+
+ f"({Balance.get_unit(destination_netuid)}/{Balance.get_unit(origin_netuid)})"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Create and display table
|
|
170
|
+
table = Table(
|
|
171
|
+
title=(
|
|
172
|
+
f"\n[{COLOR_PALETTE.G.HEADER}]"
|
|
173
|
+
f"Moving stake from: "
|
|
174
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}]{Balance.get_unit(origin_netuid)}(Netuid: {origin_netuid})"
|
|
175
|
+
f"[/{COLOR_PALETTE.G.SUBHEAD}] "
|
|
176
|
+
f"to: "
|
|
177
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}]{Balance.get_unit(destination_netuid)}(Netuid: {destination_netuid})"
|
|
178
|
+
f"[/{COLOR_PALETTE.G.SUBHEAD}]\nNetwork: {meshtensor.network}\n"
|
|
179
|
+
f"[/{COLOR_PALETTE.G.HEADER}]"
|
|
180
|
+
),
|
|
181
|
+
show_footer=True,
|
|
182
|
+
show_edge=False,
|
|
183
|
+
header_style="bold white",
|
|
184
|
+
border_style="bright_black",
|
|
185
|
+
style="bold",
|
|
186
|
+
title_justify="center",
|
|
187
|
+
show_lines=False,
|
|
188
|
+
pad_edge=True,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
table.add_column(
|
|
192
|
+
"origin netuid",
|
|
193
|
+
justify="center",
|
|
194
|
+
style=COLOR_PALETTE["GENERAL"]["SYMBOL"],
|
|
195
|
+
max_width=14,
|
|
196
|
+
)
|
|
197
|
+
table.add_column(
|
|
198
|
+
"origin hotkey",
|
|
199
|
+
justify="center",
|
|
200
|
+
style=COLOR_PALETTE["GENERAL"]["HOTKEY"],
|
|
201
|
+
max_width=15,
|
|
202
|
+
)
|
|
203
|
+
table.add_column(
|
|
204
|
+
"dest netuid",
|
|
205
|
+
justify="center",
|
|
206
|
+
style=COLOR_PALETTE["GENERAL"]["SYMBOL"],
|
|
207
|
+
max_width=12,
|
|
208
|
+
)
|
|
209
|
+
table.add_column(
|
|
210
|
+
"dest hotkey",
|
|
211
|
+
justify="center",
|
|
212
|
+
style=COLOR_PALETTE["GENERAL"]["HOTKEY"],
|
|
213
|
+
max_width=15,
|
|
214
|
+
)
|
|
215
|
+
table.add_column(
|
|
216
|
+
f"amount ({Balance.get_unit(origin_netuid)})",
|
|
217
|
+
justify="center",
|
|
218
|
+
style=COLOR_PALETTE["STAKE"]["MESH"],
|
|
219
|
+
max_width=18,
|
|
220
|
+
)
|
|
221
|
+
table.add_column(
|
|
222
|
+
f"rate ({Balance.get_unit(destination_netuid)}/{Balance.get_unit(origin_netuid)})",
|
|
223
|
+
justify="center",
|
|
224
|
+
style=COLOR_PALETTE["POOLS"]["RATE"],
|
|
225
|
+
max_width=20,
|
|
226
|
+
)
|
|
227
|
+
table.add_column(
|
|
228
|
+
f"received ({Balance.get_unit(destination_netuid)})",
|
|
229
|
+
justify="center",
|
|
230
|
+
style=COLOR_PALETTE["POOLS"]["MESH_EQUIV"],
|
|
231
|
+
max_width=18,
|
|
232
|
+
)
|
|
233
|
+
table.add_column(
|
|
234
|
+
f"Fee ({Balance.get_unit(origin_netuid)})",
|
|
235
|
+
justify="center",
|
|
236
|
+
style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"],
|
|
237
|
+
max_width=15,
|
|
238
|
+
)
|
|
239
|
+
table.add_column(
|
|
240
|
+
"Extrinsic Fee (τ)",
|
|
241
|
+
justify="center",
|
|
242
|
+
style=COLOR_PALETTE.STAKE.MESH,
|
|
243
|
+
max_width=18,
|
|
244
|
+
)
|
|
245
|
+
if safe_staking:
|
|
246
|
+
table.add_column(
|
|
247
|
+
f"Rate with tolerance: [blue]({rate_tolerance * 100}%)[/blue]",
|
|
248
|
+
justify="center",
|
|
249
|
+
style=COLOR_PALETTE["POOLS"]["RATE"],
|
|
250
|
+
)
|
|
251
|
+
table.add_column(
|
|
252
|
+
"Partial stake enabled",
|
|
253
|
+
justify="center",
|
|
254
|
+
style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"],
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
row = [
|
|
258
|
+
f"{Balance.get_unit(origin_netuid)}({origin_netuid})",
|
|
259
|
+
f"{origin_hotkey[:3]}...{origin_hotkey[-3:]}",
|
|
260
|
+
f"{Balance.get_unit(destination_netuid)}({destination_netuid})",
|
|
261
|
+
f"{destination_hotkey[:3]}...{destination_hotkey[-3:]}",
|
|
262
|
+
str(amount_to_move),
|
|
263
|
+
price_str,
|
|
264
|
+
str(received_amount),
|
|
265
|
+
str(stake_fee.set_unit(origin_netuid)),
|
|
266
|
+
str(extrinsic_fee),
|
|
267
|
+
]
|
|
268
|
+
if safe_staking:
|
|
269
|
+
rate_with_tolerance_str = (
|
|
270
|
+
f"{pricing.rate_with_tolerance:.5f}"
|
|
271
|
+
+ f"({Balance.get_unit(destination_netuid)}/{Balance.get_unit(origin_netuid)})"
|
|
272
|
+
)
|
|
273
|
+
row.extend(
|
|
274
|
+
[
|
|
275
|
+
rate_with_tolerance_str,
|
|
276
|
+
"Yes" if allow_partial_stake else "No",
|
|
277
|
+
]
|
|
278
|
+
)
|
|
279
|
+
table.add_row(*row)
|
|
280
|
+
|
|
281
|
+
console.print(table)
|
|
282
|
+
|
|
283
|
+
return received_amount, price_str
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def prompt_stake_amount(
|
|
287
|
+
current_balance: Balance, netuid: int, action_name: str
|
|
288
|
+
) -> tuple[Balance, bool]:
|
|
289
|
+
"""Prompts user to input a stake amount with validation.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
current_balance (Balance): The maximum available balance
|
|
293
|
+
netuid (int): The subnet id to get the correct unit
|
|
294
|
+
action_name (str): The name of the action (e.g. "transfer", "move", "unstake")
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
tuple[Balance, bool]: (The amount to use as Balance object, whether all balance was selected)
|
|
298
|
+
"""
|
|
299
|
+
while True:
|
|
300
|
+
amount_input = Prompt.ask(
|
|
301
|
+
f"\nEnter the amount to {action_name} "
|
|
302
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}]{Balance.get_unit(netuid)}[/{COLOR_PALETTE.S.STAKE_AMOUNT}] "
|
|
303
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}](max: {current_balance})[/{COLOR_PALETTE.S.STAKE_AMOUNT}] "
|
|
304
|
+
f"or "
|
|
305
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}]'all'[/{COLOR_PALETTE.S.STAKE_AMOUNT}] "
|
|
306
|
+
f"for entire balance"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
if amount_input.lower() == "all":
|
|
310
|
+
return current_balance, True
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
amount = float(amount_input)
|
|
314
|
+
if amount <= 0:
|
|
315
|
+
console.print("[red]Amount must be greater than 0[/red]")
|
|
316
|
+
continue
|
|
317
|
+
if amount > current_balance.tao:
|
|
318
|
+
console.print(
|
|
319
|
+
f"[red]Amount exceeds available balance of "
|
|
320
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}]{current_balance}[/{COLOR_PALETTE.S.STAKE_AMOUNT}]"
|
|
321
|
+
f"[/red]"
|
|
322
|
+
)
|
|
323
|
+
continue
|
|
324
|
+
return Balance.from_tao(amount), False
|
|
325
|
+
except ValueError:
|
|
326
|
+
console.print("[red]Please enter a valid number or 'all'[/red]")
|
|
327
|
+
# can never return this, but fixes the type checker
|
|
328
|
+
return Balance(0), False
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
async def stake_move_transfer_selection(
|
|
332
|
+
meshtensor: "MeshtensorInterface",
|
|
333
|
+
wallet: Wallet,
|
|
334
|
+
):
|
|
335
|
+
"""Selection interface for moving stakes between hotkeys and subnets."""
|
|
336
|
+
stakes, ck_hk_identities, old_identities = await asyncio.gather(
|
|
337
|
+
meshtensor.get_stake_for_coldkey(coldkey_ss58=wallet.coldkeypub.ss58_address),
|
|
338
|
+
meshtensor.fetch_coldkey_hotkey_identities(),
|
|
339
|
+
meshtensor.get_delegate_identities(),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
hotkey_stakes = {}
|
|
343
|
+
for stake in stakes:
|
|
344
|
+
if stake.stake.tao > 0:
|
|
345
|
+
hotkey = stake.hotkey_ss58
|
|
346
|
+
netuid = stake.netuid
|
|
347
|
+
stake_balance = stake.stake
|
|
348
|
+
hotkey_stakes.setdefault(hotkey, {})[netuid] = stake_balance
|
|
349
|
+
|
|
350
|
+
if not hotkey_stakes:
|
|
351
|
+
print_error("You have no stakes to move.")
|
|
352
|
+
raise ValueError
|
|
353
|
+
|
|
354
|
+
# Display hotkeys with stakes
|
|
355
|
+
table = Table(
|
|
356
|
+
title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Hotkeys with Stakes\n",
|
|
357
|
+
show_footer=True,
|
|
358
|
+
show_edge=False,
|
|
359
|
+
header_style="bold white",
|
|
360
|
+
border_style="bright_black",
|
|
361
|
+
style="bold",
|
|
362
|
+
title_justify="center",
|
|
363
|
+
show_lines=False,
|
|
364
|
+
pad_edge=True,
|
|
365
|
+
)
|
|
366
|
+
table.add_column("Index", justify="right")
|
|
367
|
+
table.add_column("Identity", style=COLOR_PALETTE["GENERAL"]["SUBHEADING"])
|
|
368
|
+
table.add_column("Netuids", style=COLOR_PALETTE["GENERAL"]["NETUID"])
|
|
369
|
+
table.add_column("Hotkey Address", style=COLOR_PALETTE["GENERAL"]["HOTKEY"])
|
|
370
|
+
|
|
371
|
+
hotkeys_info = []
|
|
372
|
+
for idx, (hotkey_ss58, netuid_stakes) in enumerate(hotkey_stakes.items()):
|
|
373
|
+
if hk_identity := ck_hk_identities["hotkeys"].get(hotkey_ss58):
|
|
374
|
+
hotkey_name = hk_identity.get("identity", {}).get(
|
|
375
|
+
"name", ""
|
|
376
|
+
) or hk_identity.get("display", "~")
|
|
377
|
+
elif old_identity := old_identities.get(hotkey_ss58):
|
|
378
|
+
hotkey_name = old_identity.display
|
|
379
|
+
else:
|
|
380
|
+
hotkey_name = "~"
|
|
381
|
+
hotkeys_info.append(
|
|
382
|
+
{
|
|
383
|
+
"index": idx,
|
|
384
|
+
"identity": hotkey_name,
|
|
385
|
+
"hotkey_ss58": hotkey_ss58,
|
|
386
|
+
"netuids": list(netuid_stakes.keys()),
|
|
387
|
+
"stakes": netuid_stakes,
|
|
388
|
+
}
|
|
389
|
+
)
|
|
390
|
+
table.add_row(
|
|
391
|
+
str(idx),
|
|
392
|
+
hotkey_name,
|
|
393
|
+
group_subnets([n for n in netuid_stakes.keys()]),
|
|
394
|
+
hotkey_ss58,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
console.print("\n", table)
|
|
398
|
+
|
|
399
|
+
# Select origin hotkey
|
|
400
|
+
origin_idx = Prompt.ask(
|
|
401
|
+
"\nEnter the index of the hotkey you want to move stake from",
|
|
402
|
+
choices=[str(i) for i in range(len(hotkeys_info))],
|
|
403
|
+
)
|
|
404
|
+
origin_hotkey_info = hotkeys_info[int(origin_idx)]
|
|
405
|
+
origin_hotkey_ss58 = origin_hotkey_info["hotkey_ss58"]
|
|
406
|
+
|
|
407
|
+
# Display available netuids for selected hotkey
|
|
408
|
+
table = Table(
|
|
409
|
+
title=f"\n[{COLOR_PALETTE.G.HEADER}]Available Stakes for Hotkey\n[/{COLOR_PALETTE.G.HEADER}]"
|
|
410
|
+
f"[{COLOR_PALETTE.G.HK}]{origin_hotkey_ss58}[/{COLOR_PALETTE.G.HK}]\n",
|
|
411
|
+
show_edge=False,
|
|
412
|
+
header_style="bold white",
|
|
413
|
+
border_style="bright_black",
|
|
414
|
+
title_justify="center",
|
|
415
|
+
width=len(origin_hotkey_ss58) + 20,
|
|
416
|
+
)
|
|
417
|
+
table.add_column("Netuid", style="cyan")
|
|
418
|
+
table.add_column("Stake Amount", style=COLOR_PALETTE.STAKE.STAKE_AMOUNT)
|
|
419
|
+
|
|
420
|
+
available_netuids = []
|
|
421
|
+
for netuid in origin_hotkey_info["netuids"]:
|
|
422
|
+
stake = origin_hotkey_info["stakes"][netuid]
|
|
423
|
+
if stake.tao > 0:
|
|
424
|
+
available_netuids.append(netuid)
|
|
425
|
+
table.add_row(str(netuid), str(stake))
|
|
426
|
+
|
|
427
|
+
console.print("\n", table)
|
|
428
|
+
|
|
429
|
+
# Select origin netuid
|
|
430
|
+
origin_netuid = Prompt.ask(
|
|
431
|
+
"\nEnter the netuid you want to move stake from",
|
|
432
|
+
choices=[str(netuid) for netuid in available_netuids],
|
|
433
|
+
)
|
|
434
|
+
origin_netuid = int(origin_netuid)
|
|
435
|
+
origin_stake = origin_hotkey_info["stakes"][origin_netuid]
|
|
436
|
+
|
|
437
|
+
# Ask for amount to move
|
|
438
|
+
amount, stake_all = prompt_stake_amount(origin_stake, origin_netuid, "move")
|
|
439
|
+
|
|
440
|
+
all_subnets = sorted(await meshtensor.get_all_subnet_netuids())
|
|
441
|
+
destination_netuid = Prompt.ask(
|
|
442
|
+
"\nEnter the netuid of the subnet you want to move stake to"
|
|
443
|
+
+ f" ([dim]{group_subnets(all_subnets)}[/dim])",
|
|
444
|
+
choices=[str(netuid) for netuid in all_subnets],
|
|
445
|
+
show_choices=False,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
"origin_hotkey": origin_hotkey_ss58,
|
|
450
|
+
"origin_netuid": origin_netuid,
|
|
451
|
+
"amount": amount.tao,
|
|
452
|
+
"stake_all": stake_all,
|
|
453
|
+
"destination_netuid": int(destination_netuid),
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
async def stake_swap_selection(
|
|
458
|
+
meshtensor: "MeshtensorInterface",
|
|
459
|
+
wallet: Wallet,
|
|
460
|
+
) -> dict:
|
|
461
|
+
"""Selection interface for swapping stakes between subnets."""
|
|
462
|
+
block_hash = await meshtensor.substrate.get_chain_head()
|
|
463
|
+
stakes, all_subnets = await asyncio.gather(
|
|
464
|
+
meshtensor.get_stake_for_coldkey(
|
|
465
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address, block_hash=block_hash
|
|
466
|
+
),
|
|
467
|
+
meshtensor.all_subnets(block_hash=block_hash),
|
|
468
|
+
)
|
|
469
|
+
subnet_dict = {di.netuid: di for di in all_subnets}
|
|
470
|
+
|
|
471
|
+
# Filter stakes for this hotkey
|
|
472
|
+
hotkey_stakes = {}
|
|
473
|
+
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
|
|
474
|
+
for stake in stakes:
|
|
475
|
+
if stake.hotkey_ss58 == hotkey_ss58 and stake.stake.tao > 0:
|
|
476
|
+
hotkey_stakes[stake.netuid] = {
|
|
477
|
+
"stake": stake.stake,
|
|
478
|
+
"is_registered": stake.is_registered,
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if not hotkey_stakes:
|
|
482
|
+
print_error(f"No stakes found for hotkey: {wallet.hotkey_str}")
|
|
483
|
+
raise ValueError
|
|
484
|
+
|
|
485
|
+
# Display available stakes
|
|
486
|
+
table = Table(
|
|
487
|
+
title=f"\n[{COLOR_PALETTE.G.HEADER}]Available Stakes for Hotkey\n[/{COLOR_PALETTE.G.HEADER}]"
|
|
488
|
+
f"[{COLOR_PALETTE.G.HK}]{wallet.hotkey_str}: {hotkey_ss58}[/{COLOR_PALETTE.G.HK}]\n",
|
|
489
|
+
show_edge=False,
|
|
490
|
+
header_style="bold white",
|
|
491
|
+
border_style="bright_black",
|
|
492
|
+
title_justify="center",
|
|
493
|
+
width=len(hotkey_ss58) + 20,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
table.add_column("Netuid", style=COLOR_PALETTE["GENERAL"]["NETUID"])
|
|
497
|
+
table.add_column("Name", style="cyan", justify="left")
|
|
498
|
+
table.add_column("Stake Amount", style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"])
|
|
499
|
+
table.add_column("Registered", justify="center")
|
|
500
|
+
|
|
501
|
+
available_netuids = []
|
|
502
|
+
for netuid, stake_info in sorted(hotkey_stakes.items()):
|
|
503
|
+
subnet_info = subnet_dict[netuid]
|
|
504
|
+
subnet_name_cell = (
|
|
505
|
+
f"[{COLOR_PALETTE.G.SYM}]{subnet_info.symbol if netuid != 0 else 'τ'}[/{COLOR_PALETTE.G.SYM}]"
|
|
506
|
+
f" {get_subnet_name(subnet_info)}"
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
available_netuids.append(netuid)
|
|
510
|
+
table.add_row(
|
|
511
|
+
str(netuid),
|
|
512
|
+
subnet_name_cell,
|
|
513
|
+
str(stake_info["stake"]),
|
|
514
|
+
"[dark_sea_green3]YES"
|
|
515
|
+
if stake_info["is_registered"]
|
|
516
|
+
else f"[{COLOR_PALETTE['STAKE']['NOT_REGISTERED']}]NO",
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
console.print("\n", table)
|
|
520
|
+
|
|
521
|
+
# Select origin netuid
|
|
522
|
+
origin_netuid = Prompt.ask(
|
|
523
|
+
"\nEnter the netuid of the subnet you want to swap stake from"
|
|
524
|
+
+ f" ([dim]{group_subnets(sorted(available_netuids))}[/dim])",
|
|
525
|
+
choices=[str(netuid) for netuid in available_netuids],
|
|
526
|
+
show_choices=False,
|
|
527
|
+
)
|
|
528
|
+
origin_netuid = int(origin_netuid)
|
|
529
|
+
origin_stake = hotkey_stakes[origin_netuid]["stake"]
|
|
530
|
+
|
|
531
|
+
# Ask for amount to swap
|
|
532
|
+
amount, _ = prompt_stake_amount(origin_stake, origin_netuid, "swap")
|
|
533
|
+
|
|
534
|
+
all_netuids = sorted(await meshtensor.get_all_subnet_netuids())
|
|
535
|
+
destination_netuids = [netuid for netuid in all_netuids if netuid != origin_netuid]
|
|
536
|
+
destination_choices = [str(netuid) for netuid in destination_netuids]
|
|
537
|
+
destination_netuid = Prompt.ask(
|
|
538
|
+
"\nEnter the netuid of the subnet you want to swap stake to"
|
|
539
|
+
+ f" ([dim]{group_subnets(destination_netuids)}[/dim])",
|
|
540
|
+
choices=destination_choices,
|
|
541
|
+
show_choices=False,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
"origin_netuid": origin_netuid,
|
|
546
|
+
"amount": amount.tao,
|
|
547
|
+
"destination_netuid": int(destination_netuid),
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# Commands
|
|
552
|
+
async def move_stake(
|
|
553
|
+
meshtensor: "MeshtensorInterface",
|
|
554
|
+
wallet: Wallet,
|
|
555
|
+
origin_netuid: int,
|
|
556
|
+
origin_hotkey: str,
|
|
557
|
+
destination_netuid: int,
|
|
558
|
+
destination_hotkey: str,
|
|
559
|
+
amount: float,
|
|
560
|
+
stake_all: bool,
|
|
561
|
+
era: int,
|
|
562
|
+
interactive_selection: bool = False,
|
|
563
|
+
prompt: bool = True,
|
|
564
|
+
decline: bool = False,
|
|
565
|
+
quiet: bool = False,
|
|
566
|
+
proxy: Optional[str] = None,
|
|
567
|
+
mev_protection: bool = True,
|
|
568
|
+
) -> tuple[bool, str]:
|
|
569
|
+
if interactive_selection:
|
|
570
|
+
try:
|
|
571
|
+
selection = await stake_move_transfer_selection(meshtensor, wallet)
|
|
572
|
+
except ValueError:
|
|
573
|
+
return False, ""
|
|
574
|
+
origin_hotkey = selection["origin_hotkey"]
|
|
575
|
+
origin_netuid = selection["origin_netuid"]
|
|
576
|
+
amount = selection["amount"]
|
|
577
|
+
stake_all = selection["stake_all"]
|
|
578
|
+
destination_netuid = selection["destination_netuid"]
|
|
579
|
+
|
|
580
|
+
# Get the wallet stake balances.
|
|
581
|
+
block_hash = await meshtensor.substrate.get_chain_head()
|
|
582
|
+
# TODO should this use `proxy if proxy else wallet.coldkeypub.ss58_address`?
|
|
583
|
+
origin_stake_balance, destination_stake_balance = await asyncio.gather(
|
|
584
|
+
meshtensor.get_stake(
|
|
585
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
586
|
+
hotkey_ss58=origin_hotkey,
|
|
587
|
+
netuid=origin_netuid,
|
|
588
|
+
block_hash=block_hash,
|
|
589
|
+
),
|
|
590
|
+
meshtensor.get_stake(
|
|
591
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
592
|
+
hotkey_ss58=destination_hotkey,
|
|
593
|
+
netuid=destination_netuid,
|
|
594
|
+
block_hash=block_hash,
|
|
595
|
+
),
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
if origin_stake_balance.tao == 0:
|
|
599
|
+
print_error(
|
|
600
|
+
f"Your balance is "
|
|
601
|
+
f"[{COLOR_PALETTE.POOLS.MESH}]0[/{COLOR_PALETTE.POOLS.MESH}] "
|
|
602
|
+
f"in Netuid: "
|
|
603
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}]{origin_netuid}[/{COLOR_PALETTE.G.SUBHEAD}]"
|
|
604
|
+
)
|
|
605
|
+
return False, ""
|
|
606
|
+
|
|
607
|
+
console.print(
|
|
608
|
+
f"\nOrigin Netuid: "
|
|
609
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}]{origin_netuid}[/{COLOR_PALETTE.G.SUBHEAD}], "
|
|
610
|
+
f"Origin stake: "
|
|
611
|
+
f"[{COLOR_PALETTE.POOLS.MESH}]{origin_stake_balance}[/{COLOR_PALETTE.POOLS.MESH}]"
|
|
612
|
+
)
|
|
613
|
+
console.print(
|
|
614
|
+
f"Destination netuid: "
|
|
615
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}]{destination_netuid}[/{COLOR_PALETTE.G.SUBHEAD}], "
|
|
616
|
+
f"Destination stake: "
|
|
617
|
+
f"[{COLOR_PALETTE.POOLS.MESH}]{destination_stake_balance}[/{COLOR_PALETTE.POOLS.MESH}]\n"
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
# Determine the amount we are moving.
|
|
621
|
+
if amount:
|
|
622
|
+
amount_to_move_as_balance = Balance.from_tao(amount)
|
|
623
|
+
elif stake_all:
|
|
624
|
+
amount_to_move_as_balance = origin_stake_balance
|
|
625
|
+
else:
|
|
626
|
+
amount_to_move_as_balance, _ = prompt_stake_amount(
|
|
627
|
+
origin_stake_balance, origin_netuid, "move"
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
# Check enough to move.
|
|
631
|
+
amount_to_move_as_balance.set_unit(origin_netuid)
|
|
632
|
+
if amount_to_move_as_balance > origin_stake_balance:
|
|
633
|
+
print_error(
|
|
634
|
+
f"Not enough stake:\n"
|
|
635
|
+
f" Stake balance: [{COLOR_PALETTE.S.AMOUNT}]{origin_stake_balance}[/{COLOR_PALETTE.S.AMOUNT}]"
|
|
636
|
+
f" < Moving amount: [{COLOR_PALETTE.S.AMOUNT}]{amount_to_move_as_balance}[/{COLOR_PALETTE.S.AMOUNT}]"
|
|
637
|
+
)
|
|
638
|
+
return False, ""
|
|
639
|
+
|
|
640
|
+
call = await meshtensor.substrate.compose_call(
|
|
641
|
+
call_module="MeshtensorModule",
|
|
642
|
+
call_function="move_stake",
|
|
643
|
+
call_params={
|
|
644
|
+
"origin_hotkey": origin_hotkey,
|
|
645
|
+
"origin_netuid": origin_netuid,
|
|
646
|
+
"destination_hotkey": destination_hotkey,
|
|
647
|
+
"destination_netuid": destination_netuid,
|
|
648
|
+
"alpha_amount": amount_to_move_as_balance.meshlet,
|
|
649
|
+
},
|
|
650
|
+
)
|
|
651
|
+
pricing, sim_swap, extrinsic_fee, next_nonce = await asyncio.gather(
|
|
652
|
+
get_movement_pricing(
|
|
653
|
+
meshtensor=meshtensor,
|
|
654
|
+
origin_netuid=origin_netuid,
|
|
655
|
+
destination_netuid=destination_netuid,
|
|
656
|
+
),
|
|
657
|
+
meshtensor.sim_swap(
|
|
658
|
+
origin_netuid=origin_netuid,
|
|
659
|
+
destination_netuid=destination_netuid,
|
|
660
|
+
amount=amount_to_move_as_balance.meshlet,
|
|
661
|
+
),
|
|
662
|
+
meshtensor.get_extrinsic_fee(call, wallet.coldkeypub, proxy=proxy),
|
|
663
|
+
# TODO verify if this should be proxy or signer
|
|
664
|
+
meshtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
# Display stake movement details
|
|
668
|
+
if prompt:
|
|
669
|
+
try:
|
|
670
|
+
await display_stake_movement_cross_subnets(
|
|
671
|
+
meshtensor=meshtensor,
|
|
672
|
+
origin_netuid=origin_netuid,
|
|
673
|
+
destination_netuid=destination_netuid,
|
|
674
|
+
origin_hotkey=origin_hotkey,
|
|
675
|
+
destination_hotkey=destination_hotkey,
|
|
676
|
+
amount_to_move=amount_to_move_as_balance,
|
|
677
|
+
pricing=pricing,
|
|
678
|
+
stake_fee=sim_swap.alpha_fee
|
|
679
|
+
if origin_netuid != 0
|
|
680
|
+
else sim_swap.tao_fee,
|
|
681
|
+
extrinsic_fee=extrinsic_fee,
|
|
682
|
+
proxy=proxy,
|
|
683
|
+
)
|
|
684
|
+
except ValueError:
|
|
685
|
+
return False, ""
|
|
686
|
+
if not confirm_action(
|
|
687
|
+
"Would you like to continue?", decline=decline, quiet=quiet
|
|
688
|
+
):
|
|
689
|
+
return False, ""
|
|
690
|
+
|
|
691
|
+
# Perform moving operation.
|
|
692
|
+
if not unlock_key(wallet).success:
|
|
693
|
+
return False, ""
|
|
694
|
+
with console.status(
|
|
695
|
+
f"\n:satellite: Moving [blue]{amount_to_move_as_balance}[/blue] from [blue]{origin_hotkey}[/blue] on netuid: "
|
|
696
|
+
f"[blue]{origin_netuid}[/blue] \nto "
|
|
697
|
+
f"[blue]{destination_hotkey}[/blue] on netuid: [blue]{destination_netuid}[/blue] ..."
|
|
698
|
+
) as status:
|
|
699
|
+
success_, err_msg, response = await meshtensor.sign_and_send_extrinsic(
|
|
700
|
+
call=call,
|
|
701
|
+
wallet=wallet,
|
|
702
|
+
era={"period": era},
|
|
703
|
+
proxy=proxy,
|
|
704
|
+
mev_protection=mev_protection,
|
|
705
|
+
nonce=next_nonce,
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
ext_id = await response.get_extrinsic_identifier() if response else ""
|
|
709
|
+
if success_:
|
|
710
|
+
if mev_protection:
|
|
711
|
+
inner_hash = err_msg
|
|
712
|
+
mev_shield_id = await extract_mev_shield_id(response)
|
|
713
|
+
mev_success, mev_error, response = await wait_for_extrinsic_by_hash(
|
|
714
|
+
meshtensor=meshtensor,
|
|
715
|
+
extrinsic_hash=inner_hash,
|
|
716
|
+
shield_id=mev_shield_id,
|
|
717
|
+
submit_block_hash=response.block_hash,
|
|
718
|
+
status=status,
|
|
719
|
+
)
|
|
720
|
+
if not mev_success:
|
|
721
|
+
status.stop()
|
|
722
|
+
print_error(f"\nFailed: {mev_error}")
|
|
723
|
+
return False, ""
|
|
724
|
+
await print_extrinsic_id(response)
|
|
725
|
+
if not prompt:
|
|
726
|
+
print_success("Sent")
|
|
727
|
+
return True, ext_id
|
|
728
|
+
else:
|
|
729
|
+
print_success("[dark_sea_green3]Stake moved.[/dark_sea_green3]")
|
|
730
|
+
block_hash = await meshtensor.substrate.get_chain_head()
|
|
731
|
+
(
|
|
732
|
+
new_origin_stake_balance,
|
|
733
|
+
new_destination_stake_balance,
|
|
734
|
+
) = await asyncio.gather(
|
|
735
|
+
meshtensor.get_stake(
|
|
736
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
737
|
+
hotkey_ss58=origin_hotkey,
|
|
738
|
+
netuid=origin_netuid,
|
|
739
|
+
block_hash=block_hash,
|
|
740
|
+
),
|
|
741
|
+
meshtensor.get_stake(
|
|
742
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
743
|
+
hotkey_ss58=destination_hotkey,
|
|
744
|
+
netuid=destination_netuid,
|
|
745
|
+
block_hash=block_hash,
|
|
746
|
+
),
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
console.print(
|
|
750
|
+
f"Origin Stake:\n [blue]{origin_stake_balance}[/blue] :arrow_right: "
|
|
751
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_origin_stake_balance}"
|
|
752
|
+
)
|
|
753
|
+
console.print(
|
|
754
|
+
f"Destination Stake:\n [blue]{destination_stake_balance}[/blue] :arrow_right: "
|
|
755
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_destination_stake_balance}"
|
|
756
|
+
)
|
|
757
|
+
return True, ext_id
|
|
758
|
+
else:
|
|
759
|
+
print_error(f"\nFailed with error: {err_msg}")
|
|
760
|
+
return False, ""
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
async def transfer_stake(
|
|
764
|
+
wallet: Wallet,
|
|
765
|
+
meshtensor: "MeshtensorInterface",
|
|
766
|
+
amount: float,
|
|
767
|
+
origin_hotkey: str,
|
|
768
|
+
origin_netuid: int,
|
|
769
|
+
dest_netuid: int,
|
|
770
|
+
dest_coldkey_ss58: str,
|
|
771
|
+
era: int,
|
|
772
|
+
interactive_selection: bool = False,
|
|
773
|
+
stake_all: bool = False,
|
|
774
|
+
prompt: bool = True,
|
|
775
|
+
decline: bool = False,
|
|
776
|
+
quiet: bool = False,
|
|
777
|
+
proxy: Optional[str] = None,
|
|
778
|
+
mev_protection: bool = True,
|
|
779
|
+
) -> tuple[bool, str]:
|
|
780
|
+
"""Transfers stake from one network to another.
|
|
781
|
+
|
|
782
|
+
Args:
|
|
783
|
+
wallet: Meshtensor wallet object.
|
|
784
|
+
meshtensor: Meshtensor interface instance.
|
|
785
|
+
amount: Amount to transfer.
|
|
786
|
+
origin_hotkey: The hotkey SS58 to transfer the stake from.
|
|
787
|
+
origin_netuid: The netuid to transfer stake from.
|
|
788
|
+
dest_netuid: The netuid to transfer stake to.
|
|
789
|
+
dest_coldkey_ss58: The destination coldkey to transfer stake to.
|
|
790
|
+
interactive_selection: If true, prompts for selection of origin and destination subnets.
|
|
791
|
+
prompt: If true, prompts for confirmation before executing transfer.
|
|
792
|
+
era: number of blocks for which the extrinsic should be valid
|
|
793
|
+
stake_all: If true, transfer all stakes.
|
|
794
|
+
proxy: Optional proxy to use for this extrinsic
|
|
795
|
+
mev_protection: If true, will encrypt the extrinsic behind the mev protection shield.
|
|
796
|
+
|
|
797
|
+
Returns:
|
|
798
|
+
tuple:
|
|
799
|
+
bool: True if transfer was successful, False otherwise.
|
|
800
|
+
str: error message
|
|
801
|
+
"""
|
|
802
|
+
if interactive_selection:
|
|
803
|
+
selection = await stake_move_transfer_selection(meshtensor, wallet)
|
|
804
|
+
origin_netuid = selection["origin_netuid"]
|
|
805
|
+
amount = selection["amount"]
|
|
806
|
+
dest_netuid = selection["destination_netuid"]
|
|
807
|
+
stake_all = selection["stake_all"]
|
|
808
|
+
origin_hotkey = selection["origin_hotkey"]
|
|
809
|
+
|
|
810
|
+
# Check if both subnets exist
|
|
811
|
+
block_hash = await meshtensor.substrate.get_chain_head()
|
|
812
|
+
dest_exists, origin_exists = await asyncio.gather(
|
|
813
|
+
meshtensor.subnet_exists(netuid=dest_netuid, block_hash=block_hash),
|
|
814
|
+
meshtensor.subnet_exists(netuid=origin_netuid, block_hash=block_hash),
|
|
815
|
+
)
|
|
816
|
+
if not dest_exists:
|
|
817
|
+
print_error(f"Subnet {dest_netuid} does not exist")
|
|
818
|
+
return False, ""
|
|
819
|
+
|
|
820
|
+
if not origin_exists:
|
|
821
|
+
print_error(f"Subnet {origin_netuid} does not exist")
|
|
822
|
+
return False, ""
|
|
823
|
+
|
|
824
|
+
# Get current stake balances
|
|
825
|
+
with console.status(f"Retrieving stake data from {meshtensor.network}..."):
|
|
826
|
+
# TODO should use proxy for these checks?
|
|
827
|
+
current_stake = await meshtensor.get_stake(
|
|
828
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
829
|
+
hotkey_ss58=origin_hotkey,
|
|
830
|
+
netuid=origin_netuid,
|
|
831
|
+
)
|
|
832
|
+
current_dest_stake = await meshtensor.get_stake(
|
|
833
|
+
coldkey_ss58=dest_coldkey_ss58,
|
|
834
|
+
hotkey_ss58=origin_hotkey,
|
|
835
|
+
netuid=dest_netuid,
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
if current_stake.tao == 0:
|
|
839
|
+
print_error(
|
|
840
|
+
f"No stake found for hotkey: {origin_hotkey} on netuid: {origin_netuid}"
|
|
841
|
+
)
|
|
842
|
+
return False, ""
|
|
843
|
+
|
|
844
|
+
if amount:
|
|
845
|
+
amount_to_transfer = Balance.from_tao(amount).set_unit(origin_netuid)
|
|
846
|
+
elif stake_all:
|
|
847
|
+
amount_to_transfer = current_stake
|
|
848
|
+
else:
|
|
849
|
+
amount_to_transfer, _ = prompt_stake_amount(
|
|
850
|
+
current_stake, origin_netuid, "transfer"
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
# Check if enough stake to transfer
|
|
854
|
+
if amount_to_transfer > current_stake:
|
|
855
|
+
print_error(
|
|
856
|
+
f"Not enough stake to transfer:\n"
|
|
857
|
+
f"Stake balance: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{current_stake}[/{COLOR_PALETTE.S.STAKE_AMOUNT}] < "
|
|
858
|
+
f"Transfer amount: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{amount_to_transfer}[/{COLOR_PALETTE.S.STAKE_AMOUNT}]"
|
|
859
|
+
)
|
|
860
|
+
return False, ""
|
|
861
|
+
|
|
862
|
+
call = await meshtensor.substrate.compose_call(
|
|
863
|
+
call_module="MeshtensorModule",
|
|
864
|
+
call_function="transfer_stake",
|
|
865
|
+
call_params={
|
|
866
|
+
"destination_coldkey": dest_coldkey_ss58,
|
|
867
|
+
"hotkey": origin_hotkey,
|
|
868
|
+
"origin_netuid": origin_netuid,
|
|
869
|
+
"destination_netuid": dest_netuid,
|
|
870
|
+
"alpha_amount": amount_to_transfer.meshlet,
|
|
871
|
+
},
|
|
872
|
+
)
|
|
873
|
+
pricing, sim_swap, extrinsic_fee, next_nonce = await asyncio.gather(
|
|
874
|
+
get_movement_pricing(
|
|
875
|
+
meshtensor=meshtensor,
|
|
876
|
+
origin_netuid=origin_netuid,
|
|
877
|
+
destination_netuid=dest_netuid,
|
|
878
|
+
),
|
|
879
|
+
meshtensor.sim_swap(
|
|
880
|
+
origin_netuid=origin_netuid,
|
|
881
|
+
destination_netuid=dest_netuid,
|
|
882
|
+
amount=amount_to_transfer.meshlet,
|
|
883
|
+
),
|
|
884
|
+
meshtensor.get_extrinsic_fee(call, wallet.coldkeypub, proxy=proxy),
|
|
885
|
+
meshtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
# Display stake movement details
|
|
889
|
+
if prompt:
|
|
890
|
+
try:
|
|
891
|
+
await display_stake_movement_cross_subnets(
|
|
892
|
+
meshtensor=meshtensor,
|
|
893
|
+
origin_netuid=origin_netuid,
|
|
894
|
+
destination_netuid=dest_netuid,
|
|
895
|
+
origin_hotkey=origin_hotkey,
|
|
896
|
+
destination_hotkey=origin_hotkey,
|
|
897
|
+
amount_to_move=amount_to_transfer,
|
|
898
|
+
pricing=pricing,
|
|
899
|
+
stake_fee=sim_swap.alpha_fee
|
|
900
|
+
if origin_netuid != 0
|
|
901
|
+
else sim_swap.tao_fee,
|
|
902
|
+
extrinsic_fee=extrinsic_fee,
|
|
903
|
+
proxy=proxy,
|
|
904
|
+
)
|
|
905
|
+
except ValueError:
|
|
906
|
+
return False, ""
|
|
907
|
+
|
|
908
|
+
if not confirm_action(
|
|
909
|
+
"Would you like to continue?", decline=decline, quiet=quiet
|
|
910
|
+
):
|
|
911
|
+
return False, ""
|
|
912
|
+
|
|
913
|
+
# Perform transfer operation
|
|
914
|
+
if not unlock_key(wallet).success:
|
|
915
|
+
return False, ""
|
|
916
|
+
|
|
917
|
+
with console.status("\n:satellite: Transferring stake ...") as status:
|
|
918
|
+
success_, err_msg, response = await meshtensor.sign_and_send_extrinsic(
|
|
919
|
+
call=call,
|
|
920
|
+
wallet=wallet,
|
|
921
|
+
era={"period": era},
|
|
922
|
+
proxy=proxy,
|
|
923
|
+
mev_protection=mev_protection,
|
|
924
|
+
nonce=next_nonce,
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
if success_:
|
|
928
|
+
if mev_protection:
|
|
929
|
+
inner_hash = err_msg
|
|
930
|
+
mev_shield_id = await extract_mev_shield_id(response)
|
|
931
|
+
mev_success, mev_error, response = await wait_for_extrinsic_by_hash(
|
|
932
|
+
meshtensor=meshtensor,
|
|
933
|
+
extrinsic_hash=inner_hash,
|
|
934
|
+
shield_id=mev_shield_id,
|
|
935
|
+
submit_block_hash=response.block_hash,
|
|
936
|
+
status=status,
|
|
937
|
+
)
|
|
938
|
+
if not mev_success:
|
|
939
|
+
status.stop()
|
|
940
|
+
print_error(f"\nFailed: {mev_error}")
|
|
941
|
+
return False, ""
|
|
942
|
+
await print_extrinsic_id(response)
|
|
943
|
+
ext_id = await response.get_extrinsic_identifier()
|
|
944
|
+
if not prompt:
|
|
945
|
+
print_success("Sent")
|
|
946
|
+
return True, ext_id
|
|
947
|
+
else:
|
|
948
|
+
# Get and display new stake balances
|
|
949
|
+
new_stake, new_dest_stake = await asyncio.gather(
|
|
950
|
+
meshtensor.get_stake(
|
|
951
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
952
|
+
hotkey_ss58=origin_hotkey,
|
|
953
|
+
netuid=origin_netuid,
|
|
954
|
+
),
|
|
955
|
+
meshtensor.get_stake(
|
|
956
|
+
coldkey_ss58=dest_coldkey_ss58,
|
|
957
|
+
hotkey_ss58=origin_hotkey,
|
|
958
|
+
netuid=dest_netuid,
|
|
959
|
+
),
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
console.print(
|
|
963
|
+
f"Origin Stake:\n [blue]{current_stake}[/blue] :arrow_right: "
|
|
964
|
+
f"[{COLOR_PALETTE.S.AMOUNT}]{new_stake}"
|
|
965
|
+
)
|
|
966
|
+
console.print(
|
|
967
|
+
f"Destination Stake:\n [blue]{current_dest_stake}[/blue] :arrow_right: "
|
|
968
|
+
f"[{COLOR_PALETTE.S.AMOUNT}]{new_dest_stake}"
|
|
969
|
+
)
|
|
970
|
+
return True, ext_id
|
|
971
|
+
|
|
972
|
+
else:
|
|
973
|
+
print_error(f"Failed with error: {err_msg}")
|
|
974
|
+
return False, ""
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
async def swap_stake(
|
|
978
|
+
wallet: Wallet,
|
|
979
|
+
meshtensor: "MeshtensorInterface",
|
|
980
|
+
origin_netuid: int,
|
|
981
|
+
destination_netuid: int,
|
|
982
|
+
amount: float,
|
|
983
|
+
safe_staking: bool,
|
|
984
|
+
rate_tolerance: float,
|
|
985
|
+
allow_partial_stake: bool,
|
|
986
|
+
swap_all: bool = False,
|
|
987
|
+
era: int = 3,
|
|
988
|
+
proxy: Optional[str] = None,
|
|
989
|
+
interactive_selection: bool = False,
|
|
990
|
+
prompt: bool = True,
|
|
991
|
+
decline: bool = False,
|
|
992
|
+
quiet: bool = False,
|
|
993
|
+
wait_for_inclusion: bool = True,
|
|
994
|
+
wait_for_finalization: bool = False,
|
|
995
|
+
mev_protection: bool = True,
|
|
996
|
+
) -> tuple[bool, str]:
|
|
997
|
+
"""Swaps stake between subnets while keeping the same coldkey-hotkey pair ownership.
|
|
998
|
+
|
|
999
|
+
Args:
|
|
1000
|
+
wallet: The wallet to swap stake from.
|
|
1001
|
+
meshtensor: Meshtensor interface instance.
|
|
1002
|
+
origin_netuid: The netuid from which stake is removed.
|
|
1003
|
+
destination_netuid: The netuid to which stake is added.
|
|
1004
|
+
amount: The amount to swap.
|
|
1005
|
+
safe_staking: Whether to use safe staking with slippage limits.
|
|
1006
|
+
rate_tolerance: The maximum slippage tolerance (e.g., 0.05 for 5%).
|
|
1007
|
+
allow_partial_stake: Whether to execute the swap partially if the full amount exceeds slippage limits.
|
|
1008
|
+
swap_all: Whether to swap all stakes.
|
|
1009
|
+
era: The period (number of blocks) that the extrinsic is valid for
|
|
1010
|
+
proxy: Optional proxy to use for this extrinsic submission
|
|
1011
|
+
interactive_selection: If true, prompts for selection of origin and destination subnets.
|
|
1012
|
+
prompt: If true, prompts for confirmation before executing swap.
|
|
1013
|
+
wait_for_inclusion: If true, waits for the transaction to be included in a block.
|
|
1014
|
+
wait_for_finalization: If true, waits for the transaction to be finalized.
|
|
1015
|
+
mev_protection: If true, will encrypt the extrinsic behind the mev protection shield.
|
|
1016
|
+
|
|
1017
|
+
Returns:
|
|
1018
|
+
(success, extrinsic_identifier):
|
|
1019
|
+
success is True if the swap was successful, False otherwise.
|
|
1020
|
+
extrinsic_identifier if the extrinsic was successfully included
|
|
1021
|
+
"""
|
|
1022
|
+
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
|
|
1023
|
+
if interactive_selection:
|
|
1024
|
+
try:
|
|
1025
|
+
selection = await stake_swap_selection(meshtensor, wallet)
|
|
1026
|
+
except ValueError:
|
|
1027
|
+
return False, ""
|
|
1028
|
+
origin_netuid = selection["origin_netuid"]
|
|
1029
|
+
amount = selection["amount"]
|
|
1030
|
+
destination_netuid = selection["destination_netuid"]
|
|
1031
|
+
|
|
1032
|
+
# Check if both subnets exist
|
|
1033
|
+
block_hash = await meshtensor.substrate.get_chain_head()
|
|
1034
|
+
dest_exists, origin_exists = await asyncio.gather(
|
|
1035
|
+
meshtensor.subnet_exists(netuid=destination_netuid, block_hash=block_hash),
|
|
1036
|
+
meshtensor.subnet_exists(netuid=origin_netuid, block_hash=block_hash),
|
|
1037
|
+
)
|
|
1038
|
+
if not dest_exists:
|
|
1039
|
+
print_error(f"Subnet {destination_netuid} does not exist")
|
|
1040
|
+
return False, ""
|
|
1041
|
+
|
|
1042
|
+
if not origin_exists:
|
|
1043
|
+
print_error(f"Subnet {origin_netuid} does not exist")
|
|
1044
|
+
return False, ""
|
|
1045
|
+
|
|
1046
|
+
# Get current stake balances
|
|
1047
|
+
with console.status(f"Retrieving stake data from {meshtensor.network}..."):
|
|
1048
|
+
current_stake = await meshtensor.get_stake(
|
|
1049
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
1050
|
+
hotkey_ss58=hotkey_ss58,
|
|
1051
|
+
netuid=origin_netuid,
|
|
1052
|
+
)
|
|
1053
|
+
current_dest_stake = await meshtensor.get_stake(
|
|
1054
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
1055
|
+
hotkey_ss58=hotkey_ss58,
|
|
1056
|
+
netuid=destination_netuid,
|
|
1057
|
+
)
|
|
1058
|
+
|
|
1059
|
+
if swap_all:
|
|
1060
|
+
amount_to_swap = current_stake.set_unit(origin_netuid)
|
|
1061
|
+
else:
|
|
1062
|
+
amount_to_swap = Balance.from_tao(amount).set_unit(origin_netuid)
|
|
1063
|
+
|
|
1064
|
+
# Check if enough stake to swap
|
|
1065
|
+
if amount_to_swap > current_stake:
|
|
1066
|
+
print_error(
|
|
1067
|
+
f"Not enough stake to swap:\n"
|
|
1068
|
+
f"Stake balance: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{current_stake}[/{COLOR_PALETTE.S.STAKE_AMOUNT}] < "
|
|
1069
|
+
f"Swap amount: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{amount_to_swap}[/{COLOR_PALETTE.S.STAKE_AMOUNT}]"
|
|
1070
|
+
)
|
|
1071
|
+
return False, ""
|
|
1072
|
+
|
|
1073
|
+
pricing = await get_movement_pricing(
|
|
1074
|
+
meshtensor=meshtensor,
|
|
1075
|
+
origin_netuid=origin_netuid,
|
|
1076
|
+
destination_netuid=destination_netuid,
|
|
1077
|
+
safe_staking=safe_staking,
|
|
1078
|
+
rate_tolerance=rate_tolerance,
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
call_fn = "swap_stake"
|
|
1082
|
+
call_params = {
|
|
1083
|
+
"hotkey": hotkey_ss58,
|
|
1084
|
+
"origin_netuid": origin_netuid,
|
|
1085
|
+
"destination_netuid": destination_netuid,
|
|
1086
|
+
"alpha_amount": amount_to_swap.meshlet,
|
|
1087
|
+
}
|
|
1088
|
+
if safe_staking:
|
|
1089
|
+
if pricing.rate_with_tolerance is None:
|
|
1090
|
+
print_error("Failed to compute a rate with tolerance for safe staking.")
|
|
1091
|
+
return False, ""
|
|
1092
|
+
limit_price = Balance.from_tao(pricing.rate_with_tolerance)
|
|
1093
|
+
call_fn = "swap_stake_limit"
|
|
1094
|
+
call_params.update(
|
|
1095
|
+
{
|
|
1096
|
+
"limit_price": limit_price.meshlet,
|
|
1097
|
+
"allow_partial": allow_partial_stake,
|
|
1098
|
+
}
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
call = await meshtensor.substrate.compose_call(
|
|
1102
|
+
call_module="MeshtensorModule",
|
|
1103
|
+
call_function=call_fn,
|
|
1104
|
+
call_params=call_params,
|
|
1105
|
+
)
|
|
1106
|
+
sim_swap, extrinsic_fee, next_nonce = await asyncio.gather(
|
|
1107
|
+
meshtensor.sim_swap(
|
|
1108
|
+
origin_netuid=origin_netuid,
|
|
1109
|
+
destination_netuid=destination_netuid,
|
|
1110
|
+
amount=amount_to_swap.meshlet,
|
|
1111
|
+
),
|
|
1112
|
+
meshtensor.get_extrinsic_fee(call, wallet.coldkeypub, proxy=proxy),
|
|
1113
|
+
meshtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
# Display stake movement details
|
|
1117
|
+
if prompt:
|
|
1118
|
+
try:
|
|
1119
|
+
await display_stake_movement_cross_subnets(
|
|
1120
|
+
meshtensor=meshtensor,
|
|
1121
|
+
origin_netuid=origin_netuid,
|
|
1122
|
+
destination_netuid=destination_netuid,
|
|
1123
|
+
origin_hotkey=hotkey_ss58,
|
|
1124
|
+
destination_hotkey=hotkey_ss58,
|
|
1125
|
+
amount_to_move=amount_to_swap,
|
|
1126
|
+
pricing=pricing,
|
|
1127
|
+
stake_fee=sim_swap.alpha_fee
|
|
1128
|
+
if origin_netuid != 0
|
|
1129
|
+
else sim_swap.tao_fee,
|
|
1130
|
+
extrinsic_fee=extrinsic_fee,
|
|
1131
|
+
safe_staking=safe_staking,
|
|
1132
|
+
rate_tolerance=rate_tolerance,
|
|
1133
|
+
allow_partial_stake=allow_partial_stake,
|
|
1134
|
+
proxy=proxy,
|
|
1135
|
+
)
|
|
1136
|
+
except ValueError:
|
|
1137
|
+
return False, ""
|
|
1138
|
+
|
|
1139
|
+
if not confirm_action(
|
|
1140
|
+
"Would you like to continue?", decline=decline, quiet=quiet
|
|
1141
|
+
):
|
|
1142
|
+
return False, ""
|
|
1143
|
+
|
|
1144
|
+
# Perform swap operation
|
|
1145
|
+
if not unlock_key(wallet).success:
|
|
1146
|
+
return False, ""
|
|
1147
|
+
|
|
1148
|
+
with console.status(
|
|
1149
|
+
f"\n:satellite: Swapping stake from netuid [blue]{origin_netuid}[/blue] "
|
|
1150
|
+
f"to netuid [blue]{destination_netuid}[/blue]..."
|
|
1151
|
+
) as status:
|
|
1152
|
+
success_, err_msg, response = await meshtensor.sign_and_send_extrinsic(
|
|
1153
|
+
call=call,
|
|
1154
|
+
wallet=wallet,
|
|
1155
|
+
era={"period": era},
|
|
1156
|
+
proxy=proxy,
|
|
1157
|
+
wait_for_finalization=wait_for_finalization,
|
|
1158
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
1159
|
+
mev_protection=mev_protection,
|
|
1160
|
+
nonce=next_nonce,
|
|
1161
|
+
)
|
|
1162
|
+
|
|
1163
|
+
ext_id = await response.get_extrinsic_identifier()
|
|
1164
|
+
|
|
1165
|
+
if success_:
|
|
1166
|
+
if mev_protection:
|
|
1167
|
+
inner_hash = err_msg
|
|
1168
|
+
mev_shield_id = await extract_mev_shield_id(response)
|
|
1169
|
+
mev_success, mev_error, response = await wait_for_extrinsic_by_hash(
|
|
1170
|
+
meshtensor=meshtensor,
|
|
1171
|
+
extrinsic_hash=inner_hash,
|
|
1172
|
+
shield_id=mev_shield_id,
|
|
1173
|
+
submit_block_hash=response.block_hash,
|
|
1174
|
+
status=status,
|
|
1175
|
+
)
|
|
1176
|
+
if not mev_success:
|
|
1177
|
+
status.stop()
|
|
1178
|
+
print_error(f"\nFailed: {mev_error}")
|
|
1179
|
+
return False, ""
|
|
1180
|
+
await print_extrinsic_id(response)
|
|
1181
|
+
if not prompt:
|
|
1182
|
+
print_success("Sent")
|
|
1183
|
+
return True, await response.get_extrinsic_identifier()
|
|
1184
|
+
else:
|
|
1185
|
+
# Get and display new stake balances
|
|
1186
|
+
new_stake, new_dest_stake = await asyncio.gather(
|
|
1187
|
+
meshtensor.get_stake(
|
|
1188
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
1189
|
+
hotkey_ss58=hotkey_ss58,
|
|
1190
|
+
netuid=origin_netuid,
|
|
1191
|
+
),
|
|
1192
|
+
meshtensor.get_stake(
|
|
1193
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
|
1194
|
+
hotkey_ss58=hotkey_ss58,
|
|
1195
|
+
netuid=destination_netuid,
|
|
1196
|
+
),
|
|
1197
|
+
)
|
|
1198
|
+
|
|
1199
|
+
console.print(
|
|
1200
|
+
f"Origin Stake:\n [blue]{current_stake}[/blue] :arrow_right: "
|
|
1201
|
+
f"[{COLOR_PALETTE.S.AMOUNT}]{new_stake}"
|
|
1202
|
+
)
|
|
1203
|
+
console.print(
|
|
1204
|
+
f"Destination Stake:\n [blue]{current_dest_stake}[/blue] :arrow_right: "
|
|
1205
|
+
f"[{COLOR_PALETTE.S.AMOUNT}]{new_dest_stake}"
|
|
1206
|
+
)
|
|
1207
|
+
return True, ext_id
|
|
1208
|
+
|
|
1209
|
+
else:
|
|
1210
|
+
print_error(f"Failed with error: {err_msg}")
|
|
1211
|
+
return False, ""
|