bittensor-cli 9.19.0__tar.gz → 9.20.0__tar.gz
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.
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/PKG-INFO +1 -1
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/cli.py +39 -9
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/chain_data.py +3 -3
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/subtensor_interface.py +80 -2
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/add.py +156 -49
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/remove.py +242 -45
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/wallets.py +150 -4
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/pyproject.toml +1 -1
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/README.md +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/doc_generation_helper.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/balances.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/mev_shield.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/registration.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/root.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/serving.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/minigraph.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/networking.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/main-filters.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/main-header.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/neuron-details.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/price-multi.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/price-single.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnet-details-header.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnet-details.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.css +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.j2 +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.js +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/utils.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/axon/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/axon/axon.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/contribute.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/contributors.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/create.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/dissolve.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/refund.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/update.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/utils.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/view.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/liquidity.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/utils.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/proxy.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/auto_staking.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/children_hotkeys.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/claim.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/list.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/move.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/wizard.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/__init__.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/mechanisms.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/price.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/subnets.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/sudo.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/view.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/weights.py +0 -0
- {bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/version.py +0 -0
|
@@ -4144,7 +4144,11 @@ class CLIManager:
|
|
|
4144
4144
|
self,
|
|
4145
4145
|
action: str = typer.Argument(
|
|
4146
4146
|
None,
|
|
4147
|
-
help=
|
|
4147
|
+
help=(
|
|
4148
|
+
"Action to perform: 'announce' to announce intent, "
|
|
4149
|
+
"'execute' to complete swap after delay, 'dispute' to freeze the swap, "
|
|
4150
|
+
"'clear' to withdraw announcement, 'check' to view status."
|
|
4151
|
+
),
|
|
4148
4152
|
),
|
|
4149
4153
|
wallet_name: Optional[str] = Options.wallet_name,
|
|
4150
4154
|
wallet_path: Optional[str] = Options.wallet_path,
|
|
@@ -4176,6 +4180,9 @@ class CLIManager:
|
|
|
4176
4180
|
If you suspect compromise, you can [bold]Dispute[/bold] an active announcement to freeze
|
|
4177
4181
|
all activity for the coldkey until the triumvirate can intervene.
|
|
4178
4182
|
|
|
4183
|
+
If you want to withdraw your announcement, you can [bold]Clear[/bold] (withdraw) an announcement once the
|
|
4184
|
+
reannouncement delay has elapsed.
|
|
4185
|
+
|
|
4179
4186
|
EXAMPLES
|
|
4180
4187
|
|
|
4181
4188
|
Step 1 - Announce your intent to swap:
|
|
@@ -4190,9 +4197,13 @@ class CLIManager:
|
|
|
4190
4197
|
|
|
4191
4198
|
[green]$[/green] btcli wallet swap-coldkey dispute
|
|
4192
4199
|
|
|
4193
|
-
|
|
4200
|
+
Clear (withdraw) an announcement:
|
|
4201
|
+
|
|
4202
|
+
[green]$[/green] btcli wallet swap-coldkey clear
|
|
4194
4203
|
|
|
4195
|
-
|
|
4204
|
+
Check status of your swap:
|
|
4205
|
+
|
|
4206
|
+
[green]$[/green] btcli wallet swap-coldkey check
|
|
4196
4207
|
"""
|
|
4197
4208
|
self.verbosity_handler(quiet, verbose, prompt=False, json_output=False)
|
|
4198
4209
|
|
|
@@ -4201,18 +4212,19 @@ class CLIManager:
|
|
|
4201
4212
|
"\n[bold][blue]Coldkey Swap Actions:[/blue][/bold]\n"
|
|
4202
4213
|
" [dark_sea_green3]announce[/dark_sea_green3] - Start the swap process (pays fee, starts delay timer)\n"
|
|
4203
4214
|
" [dark_sea_green3]execute[/dark_sea_green3] - Complete the swap (after delay period)\n"
|
|
4204
|
-
" [dark_sea_green3]dispute[/dark_sea_green3] - Freeze the swap process if you suspect compromise\n
|
|
4205
|
-
" [
|
|
4215
|
+
" [dark_sea_green3]dispute[/dark_sea_green3] - Freeze the swap process if you suspect compromise\n"
|
|
4216
|
+
" [dark_sea_green3]clear[/dark_sea_green3] - Withdraw your swap announcement\n"
|
|
4217
|
+
" [dark_sea_green3]check[/dark_sea_green3] - Check the status of your swap\n\n"
|
|
4206
4218
|
)
|
|
4207
4219
|
action = Prompt.ask(
|
|
4208
4220
|
"Select action",
|
|
4209
|
-
choices=["announce", "execute", "dispute"],
|
|
4221
|
+
choices=["announce", "execute", "dispute", "clear", "check"],
|
|
4210
4222
|
default="announce",
|
|
4211
4223
|
)
|
|
4212
4224
|
|
|
4213
|
-
if action.lower() not in ("announce", "execute", "dispute"):
|
|
4225
|
+
if action.lower() not in ("announce", "execute", "dispute", "clear", "check"):
|
|
4214
4226
|
print_error(
|
|
4215
|
-
f"Invalid action: {action}. Must be 'announce', 'execute', or '
|
|
4227
|
+
f"Invalid action: {action}. Must be 'announce', 'execute', 'dispute', 'clear', or 'check'."
|
|
4216
4228
|
)
|
|
4217
4229
|
raise typer.Exit(1)
|
|
4218
4230
|
|
|
@@ -4233,7 +4245,7 @@ class CLIManager:
|
|
|
4233
4245
|
)
|
|
4234
4246
|
|
|
4235
4247
|
new_wallet_coldkey_ss58 = None
|
|
4236
|
-
if action
|
|
4248
|
+
if action not in ("dispute", "clear", "check"):
|
|
4237
4249
|
if not new_wallet_or_ss58:
|
|
4238
4250
|
new_wallet_or_ss58 = Prompt.ask(
|
|
4239
4251
|
"Enter the [blue]new wallet name[/blue] or [blue]SS58 address[/blue] of the new coldkey",
|
|
@@ -4285,6 +4297,24 @@ class CLIManager:
|
|
|
4285
4297
|
mev_protection=mev_protection,
|
|
4286
4298
|
)
|
|
4287
4299
|
)
|
|
4300
|
+
elif action == "clear":
|
|
4301
|
+
return self._run_command(
|
|
4302
|
+
wallets.clear_coldkey_swap_announcement(
|
|
4303
|
+
wallet=wallet,
|
|
4304
|
+
subtensor=self.initialize_chain(network),
|
|
4305
|
+
decline=decline,
|
|
4306
|
+
quiet=quiet,
|
|
4307
|
+
prompt=prompt,
|
|
4308
|
+
mev_protection=mev_protection,
|
|
4309
|
+
)
|
|
4310
|
+
)
|
|
4311
|
+
elif action == "check":
|
|
4312
|
+
return self._run_command(
|
|
4313
|
+
wallets.check_swap_status(
|
|
4314
|
+
subtensor=self.initialize_chain(network),
|
|
4315
|
+
origin_ss58=wallet.coldkeypub.ss58_address,
|
|
4316
|
+
)
|
|
4317
|
+
)
|
|
4288
4318
|
else:
|
|
4289
4319
|
return self._run_command(
|
|
4290
4320
|
wallets.execute_coldkey_swap(
|
|
@@ -76,9 +76,9 @@ def _chr_str(codes: tuple[int]) -> str:
|
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
def process_nested(
|
|
79
|
-
data: Sequence[dict[Hashable, tuple[int]]] | dict,
|
|
79
|
+
data: Sequence[dict[Hashable, tuple[int]]] | dict | Any,
|
|
80
80
|
chr_transform: Callable[[tuple[int]], str],
|
|
81
|
-
) -> list[dict[Hashable, str]] | dict[Hashable, str]:
|
|
81
|
+
) -> list[dict[Hashable, str]] | dict[Hashable, str] | Any:
|
|
82
82
|
"""Processes nested data structures by applying a transformation function to their elements."""
|
|
83
83
|
if isinstance(data, Sequence):
|
|
84
84
|
if len(data) > 0 and isinstance(data[0], dict):
|
|
@@ -93,7 +93,7 @@ def process_nested(
|
|
|
93
93
|
elif isinstance(data, dict):
|
|
94
94
|
return {k: chr_transform(v) for k, v in data.items()}
|
|
95
95
|
else:
|
|
96
|
-
|
|
96
|
+
return data
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
@dataclass
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/subtensor_interface.py
RENAMED
|
@@ -1271,8 +1271,8 @@ class SubtensorInterface:
|
|
|
1271
1271
|
err_msg = format_error_message(e)
|
|
1272
1272
|
if mev_protection and "'result': 'invalid'" in str(e).lower():
|
|
1273
1273
|
err_msg = (
|
|
1274
|
-
|
|
1275
|
-
|
|
1274
|
+
"MEV Shield extrinsic rejected as invalid. "
|
|
1275
|
+
"This usually means the MEV Shield NextKey changed between fetching and submission."
|
|
1276
1276
|
)
|
|
1277
1277
|
if proxy and "Invalid Transaction" in err_msg:
|
|
1278
1278
|
extrinsic_fee, signer_balance = await asyncio.gather(
|
|
@@ -1289,6 +1289,84 @@ class SubtensorInterface:
|
|
|
1289
1289
|
)
|
|
1290
1290
|
return False, err_msg, None
|
|
1291
1291
|
|
|
1292
|
+
async def sign_and_send_batch_extrinsic(
|
|
1293
|
+
self,
|
|
1294
|
+
calls: list[GenericCall],
|
|
1295
|
+
wallet: Wallet,
|
|
1296
|
+
wait_for_inclusion: bool = True,
|
|
1297
|
+
wait_for_finalization: bool = False,
|
|
1298
|
+
era: Optional[dict[str, int]] = None,
|
|
1299
|
+
proxy: Optional[str] = None,
|
|
1300
|
+
nonce: Optional[int] = None,
|
|
1301
|
+
sign_with: Literal["coldkey", "hotkey", "coldkeypub"] = "coldkey",
|
|
1302
|
+
announce_only: bool = False,
|
|
1303
|
+
mev_protection: bool = False,
|
|
1304
|
+
block_hash: Optional[str] = None,
|
|
1305
|
+
) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
|
|
1306
|
+
"""
|
|
1307
|
+
Wraps multiple extrinsic calls into a single Utility.batch_all transaction
|
|
1308
|
+
and submits it. This reduces fees by combining N separate transactions into one.
|
|
1309
|
+
|
|
1310
|
+
batch_all is atomic: if any call in the batch fails, the entire batch reverts.
|
|
1311
|
+
|
|
1312
|
+
For a single call, this delegates directly to sign_and_send_extrinsic without
|
|
1313
|
+
wrapping, so there's no overhead for non-batch use cases.
|
|
1314
|
+
|
|
1315
|
+
:param calls: list of prepared GenericCall objects to batch together.
|
|
1316
|
+
:param wallet: the wallet whose key will sign the extrinsic.
|
|
1317
|
+
:param wait_for_inclusion: wait until the extrinsic is included on chain.
|
|
1318
|
+
:param wait_for_finalization: wait until the extrinsic is finalized on chain.
|
|
1319
|
+
:param era: validity period in blocks for the transaction.
|
|
1320
|
+
:param proxy: the real account if using a proxy. None otherwise.
|
|
1321
|
+
:param nonce: explicit nonce for submission. Fetched automatically if None.
|
|
1322
|
+
:param sign_with: which wallet keypair signs the extrinsic.
|
|
1323
|
+
:param announce_only: make the call as a proxy announcement.
|
|
1324
|
+
:param mev_protection: encrypt the extrinsic via MEV Shield.
|
|
1325
|
+
:param block_hash: cached block hash for compose_call. Fetched if None.
|
|
1326
|
+
|
|
1327
|
+
:return: (success, error message or inner hash, extrinsic receipt | None)
|
|
1328
|
+
"""
|
|
1329
|
+
if not calls:
|
|
1330
|
+
return False, "No calls to batch", None
|
|
1331
|
+
|
|
1332
|
+
# No need to wrap a single call in a batch
|
|
1333
|
+
if len(calls) == 1:
|
|
1334
|
+
return await self.sign_and_send_extrinsic(
|
|
1335
|
+
call=calls[0],
|
|
1336
|
+
wallet=wallet,
|
|
1337
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
1338
|
+
wait_for_finalization=wait_for_finalization,
|
|
1339
|
+
era=era,
|
|
1340
|
+
proxy=proxy,
|
|
1341
|
+
nonce=nonce,
|
|
1342
|
+
sign_with=sign_with,
|
|
1343
|
+
announce_only=announce_only,
|
|
1344
|
+
mev_protection=mev_protection,
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1347
|
+
if block_hash is None:
|
|
1348
|
+
block_hash = await self.substrate.get_chain_head()
|
|
1349
|
+
|
|
1350
|
+
batch_call = await self.substrate.compose_call(
|
|
1351
|
+
call_module="Utility",
|
|
1352
|
+
call_function="batch_all",
|
|
1353
|
+
call_params={"calls": calls},
|
|
1354
|
+
block_hash=block_hash,
|
|
1355
|
+
)
|
|
1356
|
+
|
|
1357
|
+
return await self.sign_and_send_extrinsic(
|
|
1358
|
+
call=batch_call,
|
|
1359
|
+
wallet=wallet,
|
|
1360
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
1361
|
+
wait_for_finalization=wait_for_finalization,
|
|
1362
|
+
era=era,
|
|
1363
|
+
proxy=proxy,
|
|
1364
|
+
nonce=nonce,
|
|
1365
|
+
sign_with=sign_with,
|
|
1366
|
+
announce_only=announce_only,
|
|
1367
|
+
mev_protection=mev_protection,
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1292
1370
|
async def get_children(self, hotkey, netuid) -> tuple[bool, list, str]:
|
|
1293
1371
|
"""
|
|
1294
1372
|
This method retrieves the children of a given hotkey and netuid. It queries the SubtensorModule's ChildKeys
|
|
@@ -467,62 +467,169 @@ async def stake_add(
|
|
|
467
467
|
if not unlock_key(wallet).success:
|
|
468
468
|
return
|
|
469
469
|
|
|
470
|
+
# Build the list of (netuid, hotkey, amount, current_stake, price_limit) tuples
|
|
471
|
+
# that describe each staking operation we need to perform.
|
|
472
|
+
# The zip aligns netuids with amounts/balances (which are populated per
|
|
473
|
+
# hotkey-netuid pair, but the zip truncates to len(netuids), matching the
|
|
474
|
+
# original execution order). Each netuid's amount/price applies to all hotkeys.
|
|
475
|
+
operations = []
|
|
476
|
+
if safe_staking:
|
|
477
|
+
for ni, am, curr, price in zip(
|
|
478
|
+
netuids, amounts_to_stake, current_stake_balances, prices_with_tolerance
|
|
479
|
+
):
|
|
480
|
+
for _, staking_address in hotkeys_to_stake_to:
|
|
481
|
+
operations.append((ni, staking_address, am, curr, price))
|
|
482
|
+
else:
|
|
483
|
+
for ni, am, curr in zip(netuids, amounts_to_stake, current_stake_balances):
|
|
484
|
+
for _, staking_address in hotkeys_to_stake_to:
|
|
485
|
+
operations.append((ni, staking_address, am, curr, None))
|
|
486
|
+
|
|
487
|
+
total_ops = len(operations)
|
|
488
|
+
use_batch = total_ops > 1
|
|
489
|
+
|
|
470
490
|
successes = defaultdict(dict)
|
|
471
491
|
error_messages = defaultdict(dict)
|
|
472
492
|
extrinsic_ids = defaultdict(dict)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
+
|
|
494
|
+
if use_batch:
|
|
495
|
+
# Batch path: compose all calls, submit as a single Utility.batch_all transaction
|
|
496
|
+
with console.status(
|
|
497
|
+
f"\n:satellite: Batching {total_ops} stake operations on netuid(s): {netuids} ..."
|
|
498
|
+
) as status:
|
|
499
|
+
batch_block_hash = await subtensor.substrate.get_chain_head()
|
|
500
|
+
current_balance = await subtensor.get_balance(
|
|
501
|
+
coldkey_ss58, block_hash=batch_block_hash
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# compose_call with a block_hash does no I/O, so no need for gather
|
|
505
|
+
calls = []
|
|
506
|
+
for ni, hk, am, _, price in operations:
|
|
507
|
+
if safe_staking and ni != 0:
|
|
508
|
+
calls.append(
|
|
509
|
+
await subtensor.substrate.compose_call(
|
|
510
|
+
call_module="SubtensorModule",
|
|
511
|
+
call_function="add_stake_limit",
|
|
512
|
+
call_params={
|
|
513
|
+
"hotkey": hk,
|
|
514
|
+
"netuid": ni,
|
|
515
|
+
"amount_staked": am.rao,
|
|
516
|
+
"limit_price": price.rao,
|
|
517
|
+
"allow_partial": allow_partial_stake,
|
|
518
|
+
},
|
|
519
|
+
block_hash=batch_block_hash,
|
|
493
520
|
)
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
521
|
+
)
|
|
522
|
+
else:
|
|
523
|
+
calls.append(
|
|
524
|
+
await subtensor.substrate.compose_call(
|
|
525
|
+
call_module="SubtensorModule",
|
|
526
|
+
call_function="add_stake",
|
|
527
|
+
call_params={
|
|
528
|
+
"hotkey": hk,
|
|
529
|
+
"netuid": ni,
|
|
530
|
+
"amount_staked": am.rao,
|
|
531
|
+
},
|
|
532
|
+
block_hash=batch_block_hash,
|
|
502
533
|
)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
success, err_msg, response = await subtensor.sign_and_send_batch_extrinsic(
|
|
537
|
+
calls=list(calls),
|
|
538
|
+
wallet=wallet,
|
|
539
|
+
era={"period": era},
|
|
540
|
+
proxy=proxy,
|
|
541
|
+
mev_protection=mev_protection,
|
|
542
|
+
block_hash=batch_block_hash,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
if success and mev_protection:
|
|
546
|
+
inner_hash = err_msg
|
|
547
|
+
success, mev_error, response = await wait_for_extrinsic_by_hash(
|
|
548
|
+
subtensor=subtensor,
|
|
549
|
+
extrinsic_hash=inner_hash,
|
|
550
|
+
submit_block_hash=response.block_hash,
|
|
551
|
+
status=status,
|
|
514
552
|
)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
553
|
+
if not success:
|
|
554
|
+
err_msg = mev_error
|
|
555
|
+
|
|
556
|
+
# batch_all is atomic: all succeed or all revert
|
|
557
|
+
for ni, hk, am, curr, _ in operations:
|
|
558
|
+
successes[ni][hk] = success
|
|
559
|
+
error_messages[ni][hk] = "" if success else err_msg
|
|
560
|
+
|
|
522
561
|
if success:
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
562
|
+
ext_id = await response.get_extrinsic_identifier()
|
|
563
|
+
for ni, hk, _, _, _ in operations:
|
|
564
|
+
extrinsic_ids[ni][hk] = ext_id
|
|
565
|
+
|
|
566
|
+
if not json_output:
|
|
567
|
+
await print_extrinsic_id(response)
|
|
568
|
+
new_block_hash = await subtensor.substrate.get_chain_head()
|
|
569
|
+
new_balance = await subtensor.get_balance(
|
|
570
|
+
coldkey_ss58, block_hash=new_block_hash
|
|
571
|
+
)
|
|
572
|
+
print_success(
|
|
573
|
+
f"[dark_sea_green3]Batch finalized. "
|
|
574
|
+
f"Staked across {total_ops} operations.[/dark_sea_green3]"
|
|
575
|
+
)
|
|
576
|
+
console.print(
|
|
577
|
+
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: "
|
|
578
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
|
|
579
|
+
)
|
|
580
|
+
for ni, hk, am, curr, _ in operations:
|
|
581
|
+
new_stake = await subtensor.get_stake(
|
|
582
|
+
hotkey_ss58=hk,
|
|
583
|
+
coldkey_ss58=coldkey_ss58,
|
|
584
|
+
netuid=ni,
|
|
585
|
+
block_hash=new_block_hash,
|
|
586
|
+
)
|
|
587
|
+
console.print(
|
|
588
|
+
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
|
|
589
|
+
f"{ni}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
|
|
590
|
+
f"Hotkey: [{COLOR_PALETTE['GENERAL']['HOTKEY']}]{hk}"
|
|
591
|
+
f"[/{COLOR_PALETTE['GENERAL']['HOTKEY']}] "
|
|
592
|
+
f"Stake:\n"
|
|
593
|
+
f" [blue]{curr}[/blue] "
|
|
594
|
+
f":arrow_right: "
|
|
595
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}"
|
|
596
|
+
)
|
|
597
|
+
else:
|
|
598
|
+
print_error(
|
|
599
|
+
f":cross_mark: [red]Batch staking failed[/red]: {err_msg}",
|
|
600
|
+
status=status,
|
|
601
|
+
)
|
|
602
|
+
else:
|
|
603
|
+
# Single operation path: use the existing per-operation extrinsics
|
|
604
|
+
with console.status(
|
|
605
|
+
f"\n:satellite: Staking on netuid(s): {netuids} ..."
|
|
606
|
+
) as status:
|
|
607
|
+
for ni, staking_address, am, curr, price in operations:
|
|
608
|
+
if safe_staking and ni != 0:
|
|
609
|
+
result = await safe_stake_extrinsic(
|
|
610
|
+
netuid_=ni,
|
|
611
|
+
amount_=am,
|
|
612
|
+
current_stake=curr,
|
|
613
|
+
hotkey_ss58_=staking_address,
|
|
614
|
+
price_limit=price,
|
|
615
|
+
status_=status,
|
|
616
|
+
)
|
|
617
|
+
else:
|
|
618
|
+
result = await stake_extrinsic(
|
|
619
|
+
netuid_i=ni,
|
|
620
|
+
amount_=am,
|
|
621
|
+
current=curr,
|
|
622
|
+
staking_address_ss58=staking_address,
|
|
623
|
+
status_=status,
|
|
624
|
+
)
|
|
625
|
+
success, er_msg, ext_receipt = result
|
|
626
|
+
successes[ni][staking_address] = success
|
|
627
|
+
error_messages[ni][staking_address] = er_msg
|
|
628
|
+
if success and ext_receipt:
|
|
629
|
+
extrinsic_ids[ni][
|
|
630
|
+
staking_address
|
|
631
|
+
] = await ext_receipt.get_extrinsic_identifier()
|
|
632
|
+
|
|
526
633
|
if json_output:
|
|
527
634
|
json_console.print_json(
|
|
528
635
|
data={
|
|
@@ -329,43 +329,163 @@ async def unstake(
|
|
|
329
329
|
if not unlock_key(wallet).success:
|
|
330
330
|
return False
|
|
331
331
|
|
|
332
|
+
total_ops = len(unstake_operations)
|
|
333
|
+
use_batch = total_ops > 1
|
|
332
334
|
successes = []
|
|
333
|
-
with console.status("\n:satellite: Performing unstaking operations...") as status:
|
|
334
|
-
for op in unstake_operations:
|
|
335
|
-
common_args = {
|
|
336
|
-
"wallet": wallet,
|
|
337
|
-
"subtensor": subtensor,
|
|
338
|
-
"netuid": op["netuid"],
|
|
339
|
-
"amount": op["amount_to_unstake"],
|
|
340
|
-
"hotkey_ss58": op["hotkey_ss58"],
|
|
341
|
-
"status": status,
|
|
342
|
-
"era": era,
|
|
343
|
-
"proxy": proxy,
|
|
344
|
-
"mev_protection": mev_protection,
|
|
345
|
-
}
|
|
346
335
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
336
|
+
if use_batch:
|
|
337
|
+
# Batch path: compose all calls, submit as a single Utility.batch_all transaction
|
|
338
|
+
with console.status(
|
|
339
|
+
f"\n:satellite: Batching {total_ops} unstake operations..."
|
|
340
|
+
) as status:
|
|
341
|
+
batch_block_hash = await subtensor.substrate.get_chain_head()
|
|
342
|
+
current_balance = await subtensor.get_balance(
|
|
343
|
+
coldkey_ss58, block_hash=batch_block_hash
|
|
344
|
+
)
|
|
356
345
|
|
|
357
|
-
|
|
358
|
-
|
|
346
|
+
# compose_call with a block_hash does no I/O, so no need for gather
|
|
347
|
+
calls = []
|
|
348
|
+
for op in unstake_operations:
|
|
349
|
+
if safe_staking and op["netuid"] != 0:
|
|
350
|
+
calls.append(
|
|
351
|
+
await subtensor.substrate.compose_call(
|
|
352
|
+
call_module="SubtensorModule",
|
|
353
|
+
call_function="remove_stake_limit",
|
|
354
|
+
call_params={
|
|
355
|
+
"hotkey": op["hotkey_ss58"],
|
|
356
|
+
"netuid": op["netuid"],
|
|
357
|
+
"amount_unstaked": op["amount_to_unstake"].rao,
|
|
358
|
+
"limit_price": op["price_with_tolerance"],
|
|
359
|
+
"allow_partial": allow_partial_stake,
|
|
360
|
+
},
|
|
361
|
+
block_hash=batch_block_hash,
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
else:
|
|
365
|
+
calls.append(
|
|
366
|
+
await subtensor.substrate.compose_call(
|
|
367
|
+
call_module="SubtensorModule",
|
|
368
|
+
call_function="remove_stake",
|
|
369
|
+
call_params={
|
|
370
|
+
"hotkey": op["hotkey_ss58"],
|
|
371
|
+
"netuid": op["netuid"],
|
|
372
|
+
"amount_unstaked": op["amount_to_unstake"].rao,
|
|
373
|
+
},
|
|
374
|
+
block_hash=batch_block_hash,
|
|
375
|
+
)
|
|
376
|
+
)
|
|
359
377
|
|
|
360
|
-
|
|
361
|
-
|
|
378
|
+
success, err_msg, response = await subtensor.sign_and_send_batch_extrinsic(
|
|
379
|
+
calls=list(calls),
|
|
380
|
+
wallet=wallet,
|
|
381
|
+
era={"period": era},
|
|
382
|
+
proxy=proxy,
|
|
383
|
+
mev_protection=mev_protection,
|
|
384
|
+
block_hash=batch_block_hash,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if success and mev_protection:
|
|
388
|
+
inner_hash = err_msg
|
|
389
|
+
success, mev_error, response = await wait_for_extrinsic_by_hash(
|
|
390
|
+
subtensor=subtensor,
|
|
391
|
+
extrinsic_hash=inner_hash,
|
|
392
|
+
submit_block_hash=response.block_hash,
|
|
393
|
+
status=status,
|
|
394
|
+
)
|
|
395
|
+
if not success:
|
|
396
|
+
err_msg = mev_error
|
|
397
|
+
|
|
398
|
+
ext_id = (
|
|
399
|
+
await response.get_extrinsic_identifier()
|
|
400
|
+
if success and response
|
|
401
|
+
else None
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
for op in unstake_operations:
|
|
405
|
+
successes.append(
|
|
406
|
+
{
|
|
407
|
+
"netuid": op["netuid"],
|
|
408
|
+
"hotkey_ss58": op["hotkey_ss58"],
|
|
409
|
+
"unstake_amount": op["amount_to_unstake"].tao,
|
|
410
|
+
"success": success,
|
|
411
|
+
"extrinsic_identifier": ext_id,
|
|
412
|
+
}
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
if success:
|
|
416
|
+
if not json_output:
|
|
417
|
+
await print_extrinsic_id(response)
|
|
418
|
+
new_block_hash = await subtensor.substrate.get_chain_head()
|
|
419
|
+
new_balance = await subtensor.get_balance(
|
|
420
|
+
coldkey_ss58, block_hash=new_block_hash
|
|
421
|
+
)
|
|
422
|
+
print_success(
|
|
423
|
+
f"Batch finalized. Unstaked across {total_ops} operations."
|
|
424
|
+
)
|
|
425
|
+
console.print(
|
|
426
|
+
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: "
|
|
427
|
+
f"[{COLOR_PALETTE.S.AMOUNT}]{new_balance}"
|
|
428
|
+
)
|
|
429
|
+
for op in unstake_operations:
|
|
430
|
+
new_stake = await subtensor.get_stake(
|
|
431
|
+
hotkey_ss58=op["hotkey_ss58"],
|
|
432
|
+
coldkey_ss58=coldkey_ss58,
|
|
433
|
+
netuid=op["netuid"],
|
|
434
|
+
block_hash=new_block_hash,
|
|
435
|
+
)
|
|
436
|
+
console.print(
|
|
437
|
+
f"Subnet: [{COLOR_PALETTE.G.SUBHEAD}]{op['netuid']}"
|
|
438
|
+
f"[/{COLOR_PALETTE.G.SUBHEAD}] "
|
|
439
|
+
f"Hotkey: [{COLOR_PALETTE.G.HK}]{op['hotkey_ss58']}"
|
|
440
|
+
f"[/{COLOR_PALETTE.G.HK}] "
|
|
441
|
+
f"Stake:\n [blue]{op['current_stake_balance']}[/blue] "
|
|
442
|
+
f":arrow_right: [{COLOR_PALETTE.S.AMOUNT}]{new_stake}"
|
|
443
|
+
)
|
|
444
|
+
else:
|
|
445
|
+
print_error(
|
|
446
|
+
f":cross_mark: [red]Batch unstaking failed[/red]: {err_msg}",
|
|
447
|
+
status=status,
|
|
448
|
+
)
|
|
449
|
+
else:
|
|
450
|
+
# Single operation path: use the existing per-operation extrinsics
|
|
451
|
+
with console.status(
|
|
452
|
+
"\n:satellite: Performing unstaking operations..."
|
|
453
|
+
) as status:
|
|
454
|
+
for op in unstake_operations:
|
|
455
|
+
common_args = {
|
|
456
|
+
"wallet": wallet,
|
|
457
|
+
"subtensor": subtensor,
|
|
362
458
|
"netuid": op["netuid"],
|
|
459
|
+
"amount": op["amount_to_unstake"],
|
|
363
460
|
"hotkey_ss58": op["hotkey_ss58"],
|
|
364
|
-
"
|
|
365
|
-
"
|
|
366
|
-
"
|
|
461
|
+
"status": status,
|
|
462
|
+
"era": era,
|
|
463
|
+
"proxy": proxy,
|
|
464
|
+
"mev_protection": mev_protection,
|
|
367
465
|
}
|
|
368
|
-
|
|
466
|
+
|
|
467
|
+
if safe_staking and op["netuid"] != 0:
|
|
468
|
+
func = _safe_unstake_extrinsic
|
|
469
|
+
specific_args = {
|
|
470
|
+
"price_limit": op["price_with_tolerance"],
|
|
471
|
+
"allow_partial_stake": allow_partial_stake,
|
|
472
|
+
}
|
|
473
|
+
else:
|
|
474
|
+
func = _unstake_extrinsic
|
|
475
|
+
specific_args = {"current_stake": op["current_stake_balance"]}
|
|
476
|
+
|
|
477
|
+
suc, ext_receipt = await func(**common_args, **specific_args)
|
|
478
|
+
ext_id = await ext_receipt.get_extrinsic_identifier() if suc else None
|
|
479
|
+
|
|
480
|
+
successes.append(
|
|
481
|
+
{
|
|
482
|
+
"netuid": op["netuid"],
|
|
483
|
+
"hotkey_ss58": op["hotkey_ss58"],
|
|
484
|
+
"unstake_amount": op["amount_to_unstake"].tao,
|
|
485
|
+
"success": suc,
|
|
486
|
+
"extrinsic_identifier": ext_id,
|
|
487
|
+
}
|
|
488
|
+
)
|
|
369
489
|
|
|
370
490
|
console.print(
|
|
371
491
|
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]Unstaking operations completed."
|
|
@@ -504,7 +624,7 @@ async def unstake_all(
|
|
|
504
624
|
stake_amount = stake.stake
|
|
505
625
|
|
|
506
626
|
try:
|
|
507
|
-
|
|
627
|
+
_ = subnet_info.price.tao
|
|
508
628
|
extrinsic_type = (
|
|
509
629
|
"unstake_all" if not unstake_all_alpha else "unstake_all_alpha"
|
|
510
630
|
)
|
|
@@ -554,24 +674,101 @@ async def unstake_all(
|
|
|
554
674
|
if not unlock_key(wallet).success:
|
|
555
675
|
return
|
|
556
676
|
successes = {}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
677
|
+
use_batch = len(hotkey_ss58s) > 1
|
|
678
|
+
|
|
679
|
+
if use_batch:
|
|
680
|
+
# Batch path: compose unstake_all calls for all hotkeys into one transaction
|
|
681
|
+
with console.status(
|
|
682
|
+
f"Batching unstake-all for {len(hotkey_ss58s)} hotkeys..."
|
|
683
|
+
) as status:
|
|
684
|
+
batch_block_hash = await subtensor.substrate.get_chain_head()
|
|
685
|
+
call_function = "unstake_all_alpha" if unstake_all_alpha else "unstake_all"
|
|
686
|
+
# compose_call with a block_hash does no I/O, so no need for gather
|
|
687
|
+
calls = []
|
|
688
|
+
for hk in hotkey_ss58s:
|
|
689
|
+
calls.append(
|
|
690
|
+
await subtensor.substrate.compose_call(
|
|
691
|
+
call_module="SubtensorModule",
|
|
692
|
+
call_function=call_function,
|
|
693
|
+
call_params={"hotkey": hk},
|
|
694
|
+
block_hash=batch_block_hash,
|
|
695
|
+
)
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
success, err_msg, response = await subtensor.sign_and_send_batch_extrinsic(
|
|
699
|
+
calls=list(calls),
|
|
560
700
|
wallet=wallet,
|
|
561
|
-
|
|
562
|
-
hotkey_ss58=hotkey_ss58,
|
|
563
|
-
hotkey_name=hotkey_names.get(hotkey_ss58, hotkey_ss58),
|
|
564
|
-
unstake_all_alpha=unstake_all_alpha,
|
|
565
|
-
status=status,
|
|
566
|
-
era=era,
|
|
701
|
+
era={"period": era},
|
|
567
702
|
proxy=proxy,
|
|
568
703
|
mev_protection=mev_protection,
|
|
704
|
+
block_hash=batch_block_hash,
|
|
569
705
|
)
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
706
|
+
|
|
707
|
+
if success and mev_protection:
|
|
708
|
+
inner_hash = err_msg
|
|
709
|
+
success, mev_error, response = await wait_for_extrinsic_by_hash(
|
|
710
|
+
subtensor=subtensor,
|
|
711
|
+
extrinsic_hash=inner_hash,
|
|
712
|
+
submit_block_hash=response.block_hash,
|
|
713
|
+
status=status,
|
|
714
|
+
)
|
|
715
|
+
if not success:
|
|
716
|
+
err_msg = mev_error
|
|
717
|
+
|
|
718
|
+
ext_id = (
|
|
719
|
+
await response.get_extrinsic_identifier()
|
|
720
|
+
if success and response
|
|
721
|
+
else None
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
for hk in hotkey_ss58s:
|
|
725
|
+
successes[hk] = {
|
|
726
|
+
"success": success,
|
|
727
|
+
"extrinsic_identifier": ext_id,
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if success:
|
|
731
|
+
await print_extrinsic_id(response)
|
|
732
|
+
msg_modifier = "Alpha " if unstake_all_alpha else ""
|
|
733
|
+
new_block_hash = await subtensor.substrate.get_chain_head()
|
|
734
|
+
new_balance = await subtensor.get_balance(
|
|
735
|
+
coldkey_ss58, block_hash=new_block_hash
|
|
736
|
+
)
|
|
737
|
+
print_success(
|
|
738
|
+
f"Batch finalized. Unstaked all {msg_modifier}stakes "
|
|
739
|
+
f"from {len(hotkey_ss58s)} hotkeys."
|
|
740
|
+
)
|
|
741
|
+
console.print(
|
|
742
|
+
f"Balance:\n [blue]{current_wallet_balance}[/blue] "
|
|
743
|
+
f":arrow_right: [{COLOR_PALETTE.S.AMOUNT}]{new_balance}"
|
|
744
|
+
)
|
|
745
|
+
else:
|
|
746
|
+
print_error(
|
|
747
|
+
f":cross_mark: [red]Batch unstake-all failed[/red]: {err_msg}",
|
|
748
|
+
status=status,
|
|
749
|
+
)
|
|
750
|
+
else:
|
|
751
|
+
# Single hotkey path: use existing per-hotkey extrinsic
|
|
752
|
+
with console.status("Unstaking all stakes...") as status:
|
|
753
|
+
for hotkey_ss58 in hotkey_ss58s:
|
|
754
|
+
success, ext_receipt = await _unstake_all_extrinsic(
|
|
755
|
+
wallet=wallet,
|
|
756
|
+
subtensor=subtensor,
|
|
757
|
+
hotkey_ss58=hotkey_ss58,
|
|
758
|
+
hotkey_name=hotkey_names.get(hotkey_ss58, hotkey_ss58),
|
|
759
|
+
unstake_all_alpha=unstake_all_alpha,
|
|
760
|
+
status=status,
|
|
761
|
+
era=era,
|
|
762
|
+
proxy=proxy,
|
|
763
|
+
mev_protection=mev_protection,
|
|
764
|
+
)
|
|
765
|
+
ext_id = (
|
|
766
|
+
await ext_receipt.get_extrinsic_identifier() if success else None
|
|
767
|
+
)
|
|
768
|
+
successes[hotkey_ss58] = {
|
|
769
|
+
"success": success,
|
|
770
|
+
"extrinsic_identifier": ext_id,
|
|
771
|
+
}
|
|
575
772
|
if json_output:
|
|
576
773
|
json_console.print(json.dumps({"success": successes}))
|
|
577
774
|
|
|
@@ -2224,7 +2224,7 @@ async def announce_coldkey_swap(
|
|
|
2224
2224
|
console.print(details_table)
|
|
2225
2225
|
console.print(
|
|
2226
2226
|
f"\n[dim]After the delay, run:"
|
|
2227
|
-
f"\n[green]btcli wallet swap-coldkey execute --new-coldkey {new_coldkey_ss58}[/green]"
|
|
2227
|
+
f"\n[green]btcli wallet swap-coldkey execute --new-coldkey {new_coldkey_ss58} --wallet-name {wallet.name}[/green]"
|
|
2228
2228
|
)
|
|
2229
2229
|
|
|
2230
2230
|
return True
|
|
@@ -2345,6 +2345,134 @@ async def dispute_coldkey_swap(
|
|
|
2345
2345
|
return True
|
|
2346
2346
|
|
|
2347
2347
|
|
|
2348
|
+
async def clear_coldkey_swap_announcement(
|
|
2349
|
+
wallet: Wallet,
|
|
2350
|
+
subtensor: SubtensorInterface,
|
|
2351
|
+
decline: bool = False,
|
|
2352
|
+
quiet: bool = False,
|
|
2353
|
+
prompt: bool = True,
|
|
2354
|
+
mev_protection: bool = False,
|
|
2355
|
+
) -> bool:
|
|
2356
|
+
"""Clear (withdraw) a pending coldkey swap announcement.
|
|
2357
|
+
|
|
2358
|
+
The announcement can only be cleared after the reannouncement delay has elapsed
|
|
2359
|
+
past the execution block, and the swap must not be disputed.
|
|
2360
|
+
|
|
2361
|
+
Args:
|
|
2362
|
+
wallet: Wallet that owns the announcement (must be the announcing coldkey).
|
|
2363
|
+
subtensor: Connection to the Bittensor network.
|
|
2364
|
+
decline: If True, default to declining at confirmation prompt.
|
|
2365
|
+
quiet: If True, skip confirmation prompts and proceed.
|
|
2366
|
+
prompt: If True, show confirmation prompts.
|
|
2367
|
+
mev_protection: If True, encrypt the extrinsic with MEV protection.
|
|
2368
|
+
|
|
2369
|
+
Returns:
|
|
2370
|
+
bool: True if the clear extrinsic was included successfully, else False.
|
|
2371
|
+
"""
|
|
2372
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
|
2373
|
+
announcement, dispute, current_block, reannounce_delay = await asyncio.gather(
|
|
2374
|
+
subtensor.get_coldkey_swap_announcement(
|
|
2375
|
+
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
|
2376
|
+
),
|
|
2377
|
+
subtensor.get_coldkey_swap_dispute(
|
|
2378
|
+
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
|
2379
|
+
),
|
|
2380
|
+
subtensor.substrate.get_block_number(block_hash=block_hash),
|
|
2381
|
+
subtensor.get_coldkey_swap_reannouncement_delay(block_hash=block_hash),
|
|
2382
|
+
)
|
|
2383
|
+
|
|
2384
|
+
if not announcement:
|
|
2385
|
+
print_error(
|
|
2386
|
+
f"No coldkey swap announcement found for {wallet.coldkeypub.ss58_address}.\n"
|
|
2387
|
+
"Nothing to clear."
|
|
2388
|
+
)
|
|
2389
|
+
return False
|
|
2390
|
+
|
|
2391
|
+
if dispute is not None:
|
|
2392
|
+
console.print(
|
|
2393
|
+
f"[yellow]Swap is disputed at block {dispute}.[/yellow] "
|
|
2394
|
+
"Cannot clear a disputed announcement."
|
|
2395
|
+
)
|
|
2396
|
+
return False
|
|
2397
|
+
|
|
2398
|
+
clear_block = announcement.execution_block + reannounce_delay
|
|
2399
|
+
if current_block < clear_block:
|
|
2400
|
+
remaining = clear_block - current_block
|
|
2401
|
+
console.print(
|
|
2402
|
+
f"[yellow]Cannot clear yet.[/yellow] "
|
|
2403
|
+
f"You can clear after block {clear_block} ({blocks_to_duration(remaining)} from now).\n"
|
|
2404
|
+
f"Current block: {current_block}"
|
|
2405
|
+
)
|
|
2406
|
+
return False
|
|
2407
|
+
|
|
2408
|
+
info = create_key_value_table("Clear Coldkey Swap Announcement\n")
|
|
2409
|
+
info.add_row(
|
|
2410
|
+
"Coldkey", f"[{COLORS.G.CK}]{wallet.coldkeypub.ss58_address}[/{COLORS.G.CK}]"
|
|
2411
|
+
)
|
|
2412
|
+
info.add_row("Announced Hash", f"[dim]{announcement.new_coldkey_hash}[/dim]")
|
|
2413
|
+
info.add_row("Execution Block", str(announcement.execution_block))
|
|
2414
|
+
info.add_row(
|
|
2415
|
+
"Status",
|
|
2416
|
+
"[yellow]Pending[/yellow]"
|
|
2417
|
+
if current_block < announcement.execution_block
|
|
2418
|
+
else "[green]Ready[/green]",
|
|
2419
|
+
)
|
|
2420
|
+
console.print(info)
|
|
2421
|
+
|
|
2422
|
+
if prompt and not confirm_action(
|
|
2423
|
+
"Proceed with clearing this swap announcement?",
|
|
2424
|
+
decline=decline,
|
|
2425
|
+
quiet=quiet,
|
|
2426
|
+
):
|
|
2427
|
+
return False
|
|
2428
|
+
|
|
2429
|
+
if not unlock_key(wallet).success:
|
|
2430
|
+
return False
|
|
2431
|
+
|
|
2432
|
+
with console.status(
|
|
2433
|
+
":satellite: Clearing coldkey swap announcement on-chain..."
|
|
2434
|
+
) as status:
|
|
2435
|
+
call = await subtensor.substrate.compose_call(
|
|
2436
|
+
call_module="SubtensorModule",
|
|
2437
|
+
call_function="clear_coldkey_swap_announcement",
|
|
2438
|
+
call_params={},
|
|
2439
|
+
)
|
|
2440
|
+
success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
|
|
2441
|
+
call,
|
|
2442
|
+
wallet,
|
|
2443
|
+
wait_for_inclusion=True,
|
|
2444
|
+
wait_for_finalization=True,
|
|
2445
|
+
mev_protection=mev_protection,
|
|
2446
|
+
)
|
|
2447
|
+
|
|
2448
|
+
if not success:
|
|
2449
|
+
print_error(f"Failed to clear coldkey swap announcement: {err_msg}")
|
|
2450
|
+
return False
|
|
2451
|
+
|
|
2452
|
+
if mev_protection:
|
|
2453
|
+
inner_hash = err_msg
|
|
2454
|
+
mev_success, mev_error, ext_receipt = await wait_for_extrinsic_by_hash(
|
|
2455
|
+
subtensor=subtensor,
|
|
2456
|
+
extrinsic_hash=inner_hash,
|
|
2457
|
+
submit_block_hash=ext_receipt.block_hash,
|
|
2458
|
+
status=status,
|
|
2459
|
+
)
|
|
2460
|
+
if not mev_success:
|
|
2461
|
+
print_error(
|
|
2462
|
+
f"Failed to clear coldkey swap announcement: {mev_error}",
|
|
2463
|
+
status=status,
|
|
2464
|
+
)
|
|
2465
|
+
return False
|
|
2466
|
+
|
|
2467
|
+
print_success("[dark_sea_green3]Coldkey swap announcement cleared.")
|
|
2468
|
+
await print_extrinsic_id(ext_receipt)
|
|
2469
|
+
|
|
2470
|
+
console.print(
|
|
2471
|
+
"\n[dim]Your coldkey is no longer locked by a pending swap announcement.[/dim]"
|
|
2472
|
+
)
|
|
2473
|
+
return True
|
|
2474
|
+
|
|
2475
|
+
|
|
2348
2476
|
async def execute_coldkey_swap(
|
|
2349
2477
|
wallet: Wallet,
|
|
2350
2478
|
subtensor: SubtensorInterface,
|
|
@@ -2506,10 +2634,11 @@ async def check_swap_status(
|
|
|
2506
2634
|
"""
|
|
2507
2635
|
block_hash = await subtensor.substrate.get_chain_head()
|
|
2508
2636
|
if origin_ss58:
|
|
2509
|
-
announcement, dispute, current_block = await asyncio.gather(
|
|
2637
|
+
announcement, dispute, current_block, reannounce_delay = await asyncio.gather(
|
|
2510
2638
|
subtensor.get_coldkey_swap_announcement(origin_ss58, block_hash=block_hash),
|
|
2511
2639
|
subtensor.get_coldkey_swap_dispute(origin_ss58, block_hash=block_hash),
|
|
2512
2640
|
subtensor.substrate.get_block_number(block_hash=block_hash),
|
|
2641
|
+
subtensor.get_coldkey_swap_reannouncement_delay(block_hash=block_hash),
|
|
2513
2642
|
)
|
|
2514
2643
|
if not announcement:
|
|
2515
2644
|
console.print(
|
|
@@ -2521,10 +2650,11 @@ async def check_swap_status(
|
|
|
2521
2650
|
disputes = [(origin_ss58, dispute)] if dispute is not None else []
|
|
2522
2651
|
|
|
2523
2652
|
else:
|
|
2524
|
-
announcements, disputes, current_block = await asyncio.gather(
|
|
2653
|
+
announcements, disputes, current_block, reannounce_delay = await asyncio.gather(
|
|
2525
2654
|
subtensor.get_coldkey_swap_announcements(block_hash=block_hash),
|
|
2526
2655
|
subtensor.get_coldkey_swap_disputes(block_hash=block_hash),
|
|
2527
2656
|
subtensor.substrate.get_block_number(block_hash=block_hash),
|
|
2657
|
+
subtensor.get_coldkey_swap_reannouncement_delay(block_hash=block_hash),
|
|
2528
2658
|
)
|
|
2529
2659
|
if not announcements:
|
|
2530
2660
|
console.print(
|
|
@@ -2563,6 +2693,7 @@ async def check_swap_status(
|
|
|
2563
2693
|
Column("Execution Block", justify="right", style="dark_sea_green3"),
|
|
2564
2694
|
Column("Time Remaining", justify="right", style="yellow"),
|
|
2565
2695
|
Column("Status", justify="center", style="green"),
|
|
2696
|
+
Column("Clear Announcement", justify="right", style="yellow"),
|
|
2566
2697
|
title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Pending Coldkey Swap Announcements\nCurrent Block: {current_block}\n",
|
|
2567
2698
|
show_header=True,
|
|
2568
2699
|
show_edge=False,
|
|
@@ -2577,18 +2708,28 @@ async def check_swap_status(
|
|
|
2577
2708
|
for announcement in announcements:
|
|
2578
2709
|
dispute_block = dispute_map.get(announcement.coldkey)
|
|
2579
2710
|
remaining_blocks = announcement.execution_block - current_block
|
|
2711
|
+
clear_block = announcement.execution_block + reannounce_delay
|
|
2712
|
+
clear_remaining = clear_block - current_block
|
|
2580
2713
|
if dispute_block is not None:
|
|
2581
2714
|
status = "[red]Disputed[/red]"
|
|
2582
2715
|
time_str = f"Disputed @ {dispute_block}"
|
|
2583
2716
|
status_label = "disputed"
|
|
2717
|
+
clear_str = "[red]Disputed[/red]"
|
|
2584
2718
|
elif remaining_blocks <= 0:
|
|
2585
2719
|
status = "Ready"
|
|
2586
2720
|
time_str = "[green]Ready[/green]"
|
|
2587
2721
|
status_label = "ready"
|
|
2722
|
+
if clear_remaining <= 0:
|
|
2723
|
+
clear_str = "[green]Ready[/green]"
|
|
2724
|
+
else:
|
|
2725
|
+
clear_str = (
|
|
2726
|
+
f"Block {clear_block} ({blocks_to_duration(clear_remaining)})"
|
|
2727
|
+
)
|
|
2588
2728
|
else:
|
|
2589
2729
|
status = "Pending"
|
|
2590
2730
|
time_str = blocks_to_duration(remaining_blocks)
|
|
2591
2731
|
status_label = "pending"
|
|
2732
|
+
clear_str = f"Block {clear_block} ({blocks_to_duration(clear_remaining)})"
|
|
2592
2733
|
hash_display = f"{announcement.new_coldkey_hash[:12]}...{announcement.new_coldkey_hash[-6:]}"
|
|
2593
2734
|
|
|
2594
2735
|
table.add_row(
|
|
@@ -2597,6 +2738,7 @@ async def check_swap_status(
|
|
|
2597
2738
|
str(announcement.execution_block),
|
|
2598
2739
|
time_str,
|
|
2599
2740
|
status,
|
|
2741
|
+
clear_str,
|
|
2600
2742
|
)
|
|
2601
2743
|
|
|
2602
2744
|
payload["announcements"].append(
|
|
@@ -2607,6 +2749,8 @@ async def check_swap_status(
|
|
|
2607
2749
|
"status": status_label,
|
|
2608
2750
|
"time_remaining_blocks": max(0, remaining_blocks),
|
|
2609
2751
|
"disputed_block": dispute_block,
|
|
2752
|
+
"clear_block": clear_block,
|
|
2753
|
+
"clear_remaining_blocks": max(0, clear_remaining),
|
|
2610
2754
|
}
|
|
2611
2755
|
)
|
|
2612
2756
|
|
|
@@ -2617,5 +2761,7 @@ async def check_swap_status(
|
|
|
2617
2761
|
console.print(table)
|
|
2618
2762
|
console.print(
|
|
2619
2763
|
"\n[dim]To execute a ready swap:[/dim] "
|
|
2620
|
-
"[green]btcli wallet swap-coldkey execute[/green]"
|
|
2764
|
+
"[green]btcli wallet swap-coldkey execute[/green]\n"
|
|
2765
|
+
"[dim]To clear (withdraw) an announcement:[/dim] "
|
|
2766
|
+
"[green]btcli wallet swap-coldkey clear[/green]"
|
|
2621
2767
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/mev_shield.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/registration.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/root.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/serving.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/main-filters.j2
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/main-header.j2
RENAMED
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/price-multi.j2
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/price-single.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/table.j2
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/contribute.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/contributors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/__init__.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/liquidity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/auto_staking.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/children_hotkeys.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/__init__.py
RENAMED
|
File without changes
|
{bittensor_cli-9.19.0 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/mechanisms.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|